mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-20 22:42:53 -05:00
Refactored handleViewContent so it can be reused (#21468)
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 <mike@ghost.org>
This commit is contained in:
parent
5f59ddaacc
commit
becfd13141
5 changed files with 73 additions and 74 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@tryghost/admin-x-activitypub",
|
||||
"version": "0.3.5",
|
||||
"version": "0.3.6",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
@ -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<InboxProps> = ({}) => {
|
||||
const [, setArticleContent] = useState<ObjectProperties | null>(null);
|
||||
const [, setArticleActor] = useState<ActorProperties | null>(null);
|
||||
const {layout, setFeed, setInbox} = useLayout();
|
||||
|
||||
const {getActivitiesQuery, updateActivity} = useActivitiesForUser({
|
||||
|
@ -39,42 +36,6 @@ const Inbox: React.FC<InboxProps> = ({}) => {
|
|||
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<IntersectionObserver | null>(null);
|
||||
|
@ -120,11 +81,7 @@ const Inbox: React.FC<InboxProps> = ({}) => {
|
|||
<li
|
||||
key={activity.id}
|
||||
data-test-view-article
|
||||
onClick={() => handleViewContent(
|
||||
activity.id,
|
||||
activity.object,
|
||||
getContentAuthor(activity)
|
||||
)}
|
||||
onClick={() => handleViewContent(activity, false, updateActivity)}
|
||||
>
|
||||
<FeedItem
|
||||
actor={activity.actor}
|
||||
|
@ -132,12 +89,7 @@ const Inbox: React.FC<InboxProps> = ({}) => {
|
|||
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 && (
|
||||
<div className="h-px w-full bg-grey-200"></div>
|
||||
|
|
|
@ -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<ProfileProps> = ({}) => {
|
|||
});
|
||||
};
|
||||
|
||||
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<ProfileProps> = ({}) => {
|
|||
<li
|
||||
key={activity.id}
|
||||
data-test-view-article
|
||||
onClick={() => handlePostClick(activity)}
|
||||
onClick={() => handleViewContent(activity, false)}
|
||||
>
|
||||
<FeedItem
|
||||
actor={activity.object?.attributedTo || activity.actor}
|
||||
layout={layout}
|
||||
object={activity.object}
|
||||
type={activity.type}
|
||||
onCommentClick={() => {}}
|
||||
onCommentClick={() => handleViewContent(activity, true)}
|
||||
/>
|
||||
{index < posts.length - 1 && (
|
||||
<div className="h-px w-full bg-grey-200"></div>
|
||||
|
@ -144,14 +136,14 @@ const Profile: React.FC<ProfileProps> = ({}) => {
|
|||
<li
|
||||
key={activity.id}
|
||||
data-test-view-article
|
||||
onClick={() => handlePostClick(activity)}
|
||||
onClick={() => handleViewContent(activity, false)}
|
||||
>
|
||||
<FeedItem
|
||||
actor={activity.object?.attributedTo || activity.actor}
|
||||
layout={layout}
|
||||
object={Object.assign({}, activity.object, {liked: true})}
|
||||
type={activity.type}
|
||||
onCommentClick={() => {}}
|
||||
onCommentClick={() => handleViewContent(activity, true)}
|
||||
/>
|
||||
{index < liked.length - 1 && (
|
||||
<div className="h-px w-full bg-grey-200"></div>
|
||||
|
|
|
@ -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<ArticleModalProps> = ({
|
|||
object.replyCount = object.replyCount + 1;
|
||||
}
|
||||
|
||||
const replyBoxRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (focusReply && replyBoxRef.current) {
|
||||
setTimeout(() => {
|
||||
replyBoxRef.current?.scrollIntoView({block: 'center'});
|
||||
}, 100);
|
||||
}
|
||||
}, [focusReply]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
align='right'
|
||||
|
@ -291,7 +297,7 @@ const ArticleModal: React.FC<ArticleModalProps> = ({
|
|||
</>
|
||||
);
|
||||
})}
|
||||
|
||||
|
||||
{object.type === 'Note' && (
|
||||
<FeedItem
|
||||
actor={actor}
|
||||
|
@ -314,7 +320,13 @@ const ArticleModal: React.FC<ArticleModalProps> = ({
|
|||
/>
|
||||
)}
|
||||
|
||||
<APReplyBox focused={isFocused} object={object} onNewReply={handleNewReply}/>
|
||||
<div ref={replyBoxRef}>
|
||||
<APReplyBox
|
||||
focused={isFocused}
|
||||
object={object}
|
||||
onNewReply={handleNewReply}
|
||||
/>
|
||||
</div>
|
||||
<FeedItemDivider />
|
||||
|
||||
{isLoadingThread && <LoadingIndicator size='lg' />}
|
||||
|
|
43
apps/admin-x-activitypub/src/utils/content-handlers.ts
Normal file
43
apps/admin-x-activitypub/src/utils/content-handlers.ts
Normal file
|
@ -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<Activity>) => 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;
|
||||
};
|
Loading…
Add table
Reference in a new issue