0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Updated thread implementation to work with posts in admin-x-activitypub (#22301)

refs
[AP-721](https://linear.app/ghost/issue/AP-721/update-getactivitythread-to-work-with-posts-instead-of-activities)

Updated thread implementation to work with posts instead of activities
in `admin-x-activitypub` as part of the posts migration
This commit is contained in:
Michael Barrett 2025-02-27 20:13:37 +00:00 committed by GitHub
parent b5f1a414b6
commit b1c496f46d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 22 additions and 19 deletions

View file

@ -38,8 +38,8 @@ export interface SearchResults {
accounts: AccountSearchResult[];
}
export interface ActivityThread {
items: Activity[];
export interface Thread {
posts: Post[];
}
export type ActivityPubCollectionResponse<T> = {data: T[], next: string | null};
@ -426,10 +426,10 @@ export class ActivityPubAPI {
};
}
async getThread(id: string): Promise<ActivityThread> {
async getThread(id: string): Promise<Thread> {
const url = new URL(`.ghost/activitypub/thread/${encodeURIComponent(id)}`, this.apiUrl);
const json = await this.fetchJSON(url);
return json as ActivityThread;
return json as Thread;
}
get accountApiUrl() {

View file

@ -379,10 +379,10 @@ const ArticleModal: React.FC<ArticleModalProps> = ({
const [isFocused] = useState(focusReply ? 1 : 0);
const {threadQuery, addToThread} = useThreadForUser('index', activityId);
const {data: activityThread, isLoading: isLoadingThread} = threadQuery;
const activtyThreadActivityIdx = (activityThread?.items ?? []).findIndex(item => item.object.id === activityId);
const activityThreadChildren = (activityThread?.items ?? []).slice(activtyThreadActivityIdx + 1);
const activityThreadParents = (activityThread?.items ?? []).slice(0, activtyThreadActivityIdx);
const {data: thread, isLoading: isLoadingThread} = threadQuery;
const threadPostIdx = (thread?.posts ?? []).findIndex(item => item.object.id === activityId);
const threadChildren = (thread?.posts ?? []).slice(threadPostIdx + 1);
const threadParents = (thread?.posts ?? []).slice(0, threadPostIdx);
const modalSize = width === 'narrow' ? MODAL_SIZE_SM : MODAL_SIZE_LG;
const modal = useModal();
@ -720,7 +720,7 @@ const ArticleModal: React.FC<ArticleModalProps> = ({
gridTemplateColumns: `1fr minmax(0,${currentGridWidth}) 1fr`
} : undefined}
>
{(canNavigateBack || (activityThreadParents.length > 0)) ? (
{(canNavigateBack || (threadParents.length > 0)) ? (
<div className='col-[1/2] flex items-center justify-between'>
<Button className='transition-color flex h-10 w-10 items-center justify-center rounded-full bg-white hover:bg-gray-100' icon='arrow-left' size='sm' unstyled onClick={navigateBack}/>
</div>
@ -853,7 +853,7 @@ const ArticleModal: React.FC<ArticleModalProps> = ({
)}
<div className='grow overflow-y-auto'>
<div className={`mx-auto px-8 pb-10 pt-5`} style={{maxWidth: currentMaxWidth}}>
{activityThreadParents.map((item) => {
{threadParents.map((item) => {
return (
<>
<FeedItem
@ -883,7 +883,7 @@ const ArticleModal: React.FC<ArticleModalProps> = ({
layout={'modal'}
object={object}
repostCount={object.repostCount ?? 0}
showHeader={(canNavigateBack || (activityThreadParents.length > 0))}
showHeader={(canNavigateBack || (threadParents.length > 0))}
type='Note'
onCommentClick={() => {
repliesRef.current?.scrollIntoView({
@ -938,8 +938,8 @@ const ArticleModal: React.FC<ArticleModalProps> = ({
{isLoadingThread && <LoadingIndicator size='lg' />}
<div ref={repliesRef}>
{activityThreadChildren.map((item, index) => {
const showDivider = index !== activityThreadChildren.length - 1;
{threadChildren.map((item, index) => {
const showDivider = index !== threadChildren.length - 1;
return (
<React.Fragment key={item.id}>

View file

@ -3,7 +3,6 @@ import {
type AccountSearchResult,
ActivityPubAPI,
ActivityPubCollectionResponse,
ActivityThread,
FollowAccount,
type GetAccountFollowsResponse,
type Profile,
@ -591,19 +590,23 @@ export function useThreadForUser(handle: string, id: string) {
const siteUrl = await getSiteUrl();
const api = createActivityPubAPI(handle, siteUrl);
return api.getThread(id);
return api.getThread(id).then((response) => {
return {
posts: response.posts.map(mapPostToActivity)
};
});
}
});
const addToThread = (activity: Activity) => {
// Add the activity to the thread stored in the thread query cache
queryClient.setQueryData(queryKey, (current: ActivityThread | undefined) => {
queryClient.setQueryData(queryKey, (current: {posts: Activity[]} | undefined) => {
if (!current) {
return current;
}
return {
items: [...current.items, activity]
posts: [...current.posts, activity]
};
});
};

View file

@ -30,7 +30,7 @@ const Inbox: React.FC = () => {
const activities = (data?.pages.flatMap(page => page.posts) ?? Array.from({length: 5}, (_, index) => ({id: `placeholder-${index}`, object: {}})));
const observerRef = useRef<IntersectionObserver | null>(null);
const loadMoreRef = useRef<HTMLLIElement | null>(null);
const loadMoreRef = useRef<HTMLDivElement | null>(null);
const endLoadMoreRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
@ -103,7 +103,7 @@ const Inbox: React.FC = () => {
<Separator />
)}
{index === loadMoreIndex && (
<li ref={loadMoreRef} className='h-1'></li>
<div ref={loadMoreRef} className='h-1'></div>
)}
</li>
))}