From becfd1314175d7d064128c0707eb777ed3792740 Mon Sep 17 00:00:00 2001 From: Djordje Vlaisavljevic Date: Thu, 31 Oct 2024 09:58:47 +0000 Subject: [PATCH] Refactored handleViewContent so it can be reused (#21468) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ref https://linear.app/ghost/issue/AP-540/clicking-comment-icon-on-posts-and-likes-tabs-of-your-profile-doesnt - We want to open posts in the drawer from multiple views (Inbox, Profile etc.) and this change allows us to do so by pulling `handleViewContent` from `Inbox.tsx` into a utility function. At the same time, we’ve simplified the function so it uses less props to achieve the same functionality. - Also added a simple fix for scrolling the reply-box into view when opening a long `article` by clicking on the reply icon. We probably still need to figure out a more robust solution, because the height of the `iframe` and the fact it takes some time to load it sometimes gets in the way. Co-authored-by: Michael Barrett --- apps/admin-x-activitypub/package.json | 2 +- .../src/components/Inbox.tsx | 58 ++----------------- .../src/components/Profile.tsx | 20 ++----- .../src/components/feed/ArticleModal.tsx | 24 ++++++-- .../src/utils/content-handlers.ts | 43 ++++++++++++++ 5 files changed, 73 insertions(+), 74 deletions(-) create mode 100644 apps/admin-x-activitypub/src/utils/content-handlers.ts diff --git a/apps/admin-x-activitypub/package.json b/apps/admin-x-activitypub/package.json index 074d0f9e3a..7dd067aa15 100644 --- a/apps/admin-x-activitypub/package.json +++ b/apps/admin-x-activitypub/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/admin-x-activitypub", - "version": "0.3.5", + "version": "0.3.6", "license": "MIT", "repository": { "type": "git", diff --git a/apps/admin-x-activitypub/src/components/Inbox.tsx b/apps/admin-x-activitypub/src/components/Inbox.tsx index 7df9d8db25..57ca95703f 100644 --- a/apps/admin-x-activitypub/src/components/Inbox.tsx +++ b/apps/admin-x-activitypub/src/components/Inbox.tsx @@ -1,15 +1,14 @@ import APAvatar from './global/APAvatar'; -import ActivityItem, {type Activity} from './activities/ActivityItem'; +import ActivityItem from './activities/ActivityItem'; import ActivityPubWelcomeImage from '../assets/images/ap-welcome.png'; -import ArticleModal from './feed/ArticleModal'; import FeedItem from './feed/FeedItem'; import MainNavigation from './navigation/MainNavigation'; import NiceModal from '@ebay/nice-modal-react'; -import React, {useEffect, useRef, useState} from 'react'; +import React, {useEffect, useRef} from 'react'; import ViewProfileModal from './global/ViewProfileModal'; import getUsername from '../utils/get-username'; -import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub'; import {Button, Heading, LoadingIndicator} from '@tryghost/admin-x-design-system'; +import {handleViewContent} from '../utils/content-handlers'; import {useActivitiesForUser, useSuggestedProfiles} from '../hooks/useActivityPubQueries'; import {useLayout} from '../hooks/layout'; import {useRouting} from '@tryghost/admin-x-framework/routing'; @@ -17,8 +16,6 @@ import {useRouting} from '@tryghost/admin-x-framework/routing'; interface InboxProps {} const Inbox: React.FC = ({}) => { - const [, setArticleContent] = useState(null); - const [, setArticleActor] = useState(null); const {layout, setFeed, setInbox} = useLayout(); const {getActivitiesQuery, updateActivity} = useActivitiesForUser({ @@ -39,42 +36,6 @@ const Inbox: React.FC = ({}) => { return !activity.object.inReplyTo; }); - const handleViewContent = (activityId: string, object: ObjectProperties, actor: ActorProperties, focusReply = false) => { - setArticleContent(object); - setArticleActor(actor); - NiceModal.show(ArticleModal, { - activityId, - object, - actor, - focusReply, - updateActivity - }); - }; - - function getContentAuthor(activity: Activity) { - const actor = activity.actor; - const attributedTo = activity.object.attributedTo; - - if (!attributedTo) { - return actor; - } - - if (typeof attributedTo === 'string') { - return actor; - } - - if (Array.isArray(attributedTo)) { - const found = attributedTo.find(item => typeof item !== 'string'); - if (found) { - return found; - } else { - return actor; - } - } - - return attributedTo; - } - // Intersection observer to fetch more activities when the user scrolls // to the bottom of the page const observerRef = useRef(null); @@ -120,11 +81,7 @@ const Inbox: React.FC = ({}) => {
  • handleViewContent( - activity.id, - activity.object, - getContentAuthor(activity) - )} + onClick={() => handleViewContent(activity, false, updateActivity)} > = ({}) => { layout={layout} object={activity.object} type={activity.type} - onCommentClick={() => handleViewContent( - activity.id, - activity.object, - getContentAuthor(activity), - true - )} + onCommentClick={() => handleViewContent(activity, true, updateActivity)} /> {index < activities.length - 1 && (
    diff --git a/apps/admin-x-activitypub/src/components/Profile.tsx b/apps/admin-x-activitypub/src/components/Profile.tsx index 88c559c0ca..ceab6edf52 100644 --- a/apps/admin-x-activitypub/src/components/Profile.tsx +++ b/apps/admin-x-activitypub/src/components/Profile.tsx @@ -1,5 +1,5 @@ import APAvatar from './global/APAvatar'; -import ActivityItem, {type Activity} from './activities/ActivityItem'; +import ActivityItem from './activities/ActivityItem'; import FeedItem from './feed/FeedItem'; import MainNavigation from './navigation/MainNavigation'; import NiceModal from '@ebay/nice-modal-react'; @@ -7,9 +7,9 @@ import React, {useEffect, useRef, useState} from 'react'; import getUsername from '../utils/get-username'; import {ActorProperties} from '@tryghost/admin-x-framework/api/activitypub'; -import ArticleModal from './feed/ArticleModal'; import ViewProfileModal from './global/ViewProfileModal'; import {Button, Heading, List, NoValueLabel, Tab, TabView} from '@tryghost/admin-x-design-system'; +import {handleViewContent} from '../utils/content-handlers'; import { useFollowersCountForUser, useFollowersForUser, @@ -75,14 +75,6 @@ const Profile: React.FC = ({}) => { }); }; - const handlePostClick = (activity: Activity) => { - NiceModal.show(ArticleModal, { - object: activity.object, - actor: activity.actor, - comments: activity.object.replies || [] - }); - }; - const tabs = [ { id: 'posts', @@ -99,14 +91,14 @@ const Profile: React.FC = ({}) => {
  • handlePostClick(activity)} + onClick={() => handleViewContent(activity, false)} > {}} + onCommentClick={() => handleViewContent(activity, true)} /> {index < posts.length - 1 && (
    @@ -144,14 +136,14 @@ const Profile: React.FC = ({}) => {
  • handlePostClick(activity)} + onClick={() => handleViewContent(activity, false)} > {}} + onCommentClick={() => handleViewContent(activity, true)} /> {index < liked.length - 1 && (
    diff --git a/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx index c6e89fa14e..89600497e7 100644 --- a/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx +++ b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx @@ -76,14 +76,10 @@ const ArticleBody: React.FC<{heading: string, image: string|undefined, excerpt: } function initializeResize() { - document.body.style.opacity = '0.5'; - document.body.style.transition = 'opacity 0.3s ease'; - resizeIframe(); waitForImages().then(() => { isFullyLoaded = true; - document.body.style.opacity = '1'; resizeIframe(); }); } @@ -237,6 +233,16 @@ const ArticleModal: React.FC = ({ object.replyCount = object.replyCount + 1; } + const replyBoxRef = useRef(null); + + useEffect(() => { + if (focusReply && replyBoxRef.current) { + setTimeout(() => { + replyBoxRef.current?.scrollIntoView({block: 'center'}); + }, 100); + } + }, [focusReply]); + return ( = ({ ); })} - + {object.type === 'Note' && ( = ({ /> )} - +
    + +
    {isLoadingThread && } diff --git a/apps/admin-x-activitypub/src/utils/content-handlers.ts b/apps/admin-x-activitypub/src/utils/content-handlers.ts new file mode 100644 index 0000000000..1ae3d23c76 --- /dev/null +++ b/apps/admin-x-activitypub/src/utils/content-handlers.ts @@ -0,0 +1,43 @@ +import ArticleModal from '../components/feed/ArticleModal'; +import NiceModal from '@ebay/nice-modal-react'; +import {type Activity} from '../components/activities/ActivityItem'; + +export const handleViewContent = ( + activity: Activity, + focusReply = false, + updateActivity: (id: string, updated: Partial) => void = () => {} +) => { + const authorActor = getContentAuthor(activity); + NiceModal.show(ArticleModal, { + activityId: activity.id, + object: activity.object, + actor: authorActor, + comments: Array.isArray(activity.object.replies) ? activity.object.replies : [], + focusReply, + updateActivity + }); +}; + +export const getContentAuthor = (activity: Activity) => { + const actor = activity.actor; + const attributedTo = activity.object.attributedTo; + + if (!attributedTo) { + return actor; + } + + if (typeof attributedTo === 'string') { + return actor; + } + + if (Array.isArray(attributedTo)) { + const found = attributedTo.find(item => typeof item !== 'string'); + if (found) { + return found; + } else { + return actor; + } + } + + return attributedTo; +};