nicolium: add a generalized version of useHomeTimeline
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
@ -7,9 +7,10 @@ import Status from '@/components/statuses/status';
|
||||
import Tombstone from '@/components/statuses/tombstone';
|
||||
import PlaceholderStatus from '@/features/placeholder/components/placeholder-status';
|
||||
import { useStatus } from '@/queries/statuses/use-status';
|
||||
import { type TimelineEntry, useHomeTimeline } from '@/queries/timelines/use-home-timeline';
|
||||
import { useHomeTimeline } from '@/queries/timelines/use-timelines';
|
||||
|
||||
import type { FilterContextType } from '@/queries/settings/use-filters';
|
||||
import type { TimelineEntry } from '@/queries/timelines/use-timeline';
|
||||
|
||||
interface ITimelineStatus {
|
||||
id: string;
|
||||
|
||||
@ -7,7 +7,7 @@ import type { MinifiedInteractionRequest } from './statuses/use-interaction-requ
|
||||
import type { MinifiedContext } from './statuses/use-status';
|
||||
import type { MinifiedStatusEdit } from './statuses/use-status-history';
|
||||
import type { MinifiedEmojiReaction } from './statuses/use-status-interactions';
|
||||
import type { TimelineEntry } from './timelines/use-home-timeline';
|
||||
import type { TimelineEntry } from './timelines/use-timeline';
|
||||
import type { MinifiedSuggestion } from './trends/use-suggested-accounts';
|
||||
import type {
|
||||
MinifiedAdminAccount,
|
||||
@ -34,8 +34,10 @@ import type {
|
||||
AdminRule,
|
||||
Announcement,
|
||||
Antenna,
|
||||
AntennaTimelineParams,
|
||||
Backup,
|
||||
BookmarkFolder,
|
||||
BubbleTimelineParams,
|
||||
Chat,
|
||||
Circle,
|
||||
CredentialAccount,
|
||||
@ -46,21 +48,29 @@ import type {
|
||||
Group,
|
||||
GroupRelationship,
|
||||
GroupRole,
|
||||
GroupTimelineParams,
|
||||
HashtagTimelineParams,
|
||||
HomeTimelineParams,
|
||||
InteractionPolicies,
|
||||
LinkTimelineParams,
|
||||
List,
|
||||
ListTimelineParams,
|
||||
Location,
|
||||
Marker,
|
||||
NotificationGroup,
|
||||
OauthToken,
|
||||
PaginatedResponse,
|
||||
PaginationParams,
|
||||
PlApiClient,
|
||||
Poll,
|
||||
PublicTimelineParams,
|
||||
Relationship,
|
||||
RssFeed,
|
||||
ScheduledStatus,
|
||||
Tag,
|
||||
Translation,
|
||||
TrendsLink,
|
||||
WrenchedTimelineParams,
|
||||
} from 'pl-api';
|
||||
|
||||
type TaggedKey<TKey extends readonly unknown[], TData> = DataTag<TKey, TData>;
|
||||
@ -421,7 +431,42 @@ const suggestions = {
|
||||
|
||||
const timelines = {
|
||||
root: ['timelines'] as const,
|
||||
home: ['timelines', 'home'] as TaggedKey<['timelines', 'home'], Array<TimelineEntry>>,
|
||||
home: (params?: Omit<HomeTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'home', params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
public: (local?: boolean, params?: Omit<PublicTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'public', { local, ...params }] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
hashtag: (hashtag: string, params?: Omit<HashtagTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'hashtag', hashtag, params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
link: (url: string, params?: Omit<LinkTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'link', url, params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
list: (listId: string, params?: Omit<ListTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'list', listId, params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
group: (groupId: string, params?: Omit<GroupTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'group', groupId, params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
bubble: (params?: Omit<BubbleTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'bubble', params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
antenna: (antennaId: string, params?: Omit<AntennaTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'antenna', antennaId, params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
wrenched: (params?: Omit<WrenchedTimelineParams, keyof PaginationParams>) => {
|
||||
const key = ['timelines', 'wrenched', params] as const;
|
||||
return key as TaggedKey<typeof key, Array<TimelineEntry>>;
|
||||
},
|
||||
};
|
||||
|
||||
const timelineIds = {
|
||||
|
||||
@ -3,11 +3,9 @@ import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { importEntities } from '@/actions/importer';
|
||||
import { useTimelineStream } from '@/api/hooks/streaming/use-timeline-stream';
|
||||
import { useClient } from '@/hooks/use-client';
|
||||
|
||||
import { queryKeys } from '../keys';
|
||||
|
||||
import type { PaginatedResponse, Status } from 'pl-api';
|
||||
import type { DataTag } from '@tanstack/react-query';
|
||||
import type { PaginatedResponse, Status, StreamingParams } from 'pl-api';
|
||||
|
||||
type TimelineEntry =
|
||||
| {
|
||||
@ -33,15 +31,13 @@ type TimelineEntry =
|
||||
minId?: string;
|
||||
};
|
||||
|
||||
const processPage = ({ items: statuses, next }: PaginatedResponse<Status>) => {
|
||||
const processPage = ({
|
||||
items: statuses,
|
||||
next,
|
||||
}: PaginatedResponse<Status>): Array<TimelineEntry> => {
|
||||
const timelinePage: Array<TimelineEntry> = [];
|
||||
|
||||
// if (previous) timelinePage.push({
|
||||
// type: 'page-start',
|
||||
// maxId: statuses.at(0)?.id,
|
||||
// });
|
||||
|
||||
const processStatus = (status: Status) => {
|
||||
const processStatus = (status: Status): boolean => {
|
||||
if (timelinePage.some((entry) => entry.type === 'status' && entry.id === status.id))
|
||||
return false;
|
||||
|
||||
@ -49,14 +45,13 @@ const processPage = ({ items: statuses, next }: PaginatedResponse<Status>) => {
|
||||
const inReplyToId = (status.reblog || status).in_reply_to_id;
|
||||
|
||||
if (inReplyToId) {
|
||||
const foundStatus = statuses.find((status) => (status.reblog || status).id === inReplyToId);
|
||||
const foundStatus = statuses.find((s) => (s.reblog || s).id === inReplyToId);
|
||||
|
||||
if (foundStatus) {
|
||||
if (processStatus(foundStatus)) {
|
||||
const lastEntry = timelinePage.at(-1);
|
||||
// it's always of type status but doing this to satisfy ts
|
||||
if (lastEntry?.type === 'status') lastEntry.isConnectedBottom = true;
|
||||
|
||||
isConnectedTop = true;
|
||||
}
|
||||
}
|
||||
@ -103,38 +98,49 @@ const processPage = ({ items: statuses, next }: PaginatedResponse<Status>) => {
|
||||
return timelinePage;
|
||||
};
|
||||
|
||||
const useHomeTimeline = () => {
|
||||
const client = useClient();
|
||||
type PaginationParams = { max_id?: string; min_id?: string };
|
||||
type TimelineFetcher = (params?: PaginationParams) => Promise<PaginatedResponse<Status>>;
|
||||
|
||||
interface StreamConfig {
|
||||
stream: string;
|
||||
params?: StreamingParams;
|
||||
}
|
||||
|
||||
type TimelineQueryKey = DataTag<readonly unknown[], Array<TimelineEntry>>;
|
||||
|
||||
const useTimeline = (
|
||||
queryKey: TimelineQueryKey,
|
||||
fetcher: TimelineFetcher,
|
||||
streamConfig?: StreamConfig,
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useTimelineStream('home');
|
||||
useTimelineStream(streamConfig?.stream ?? '', streamConfig?.params, !!streamConfig?.stream);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const queryKey = queryKeys.timelines.home;
|
||||
|
||||
const query = useQuery({
|
||||
queryKey,
|
||||
queryFn: async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
const response = await client.timelines.homeTimeline();
|
||||
|
||||
importEntities({ statuses: response.items });
|
||||
|
||||
return processPage(response);
|
||||
try {
|
||||
const response = await fetcher();
|
||||
importEntities({ statuses: response.items });
|
||||
return processPage(response);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const handleLoadMore = useCallback(
|
||||
async (entry: TimelineEntry) => {
|
||||
if (isLoading) return;
|
||||
if (entry.type !== 'page-end' && entry.type !== 'page-start') return;
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (entry.type !== 'page-end' && entry.type !== 'page-start') return;
|
||||
|
||||
const response = await client.timelines.homeTimeline(
|
||||
const response = await fetcher(
|
||||
entry.type === 'page-end' ? { max_id: entry.minId } : { min_id: entry.maxId },
|
||||
);
|
||||
|
||||
@ -142,20 +148,23 @@ const useHomeTimeline = () => {
|
||||
|
||||
const timelinePage = processPage(response);
|
||||
|
||||
queryClient.setQueryData(queryKeys.timelines.home, (oldData) => {
|
||||
queryClient.setQueryData(queryKey, (oldData) => {
|
||||
if (!oldData) return timelinePage;
|
||||
|
||||
const index = oldData.indexOf(entry);
|
||||
return oldData.toSpliced(index, 1, ...timelinePage);
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
//
|
||||
}
|
||||
setIsLoading(false);
|
||||
},
|
||||
[isLoading],
|
||||
[isLoading, fetcher, queryKey, queryClient],
|
||||
);
|
||||
|
||||
return useMemo(() => ({ ...query, handleLoadMore, isLoading }), [query, isLoading]);
|
||||
return useMemo(
|
||||
() => ({ ...query, handleLoadMore, isLoading }),
|
||||
[query, handleLoadMore, isLoading],
|
||||
);
|
||||
};
|
||||
|
||||
export { useHomeTimeline, type TimelineEntry };
|
||||
export { useTimeline, type TimelineEntry };
|
||||
133
packages/nicolium/src/queries/timelines/use-timelines.ts
Normal file
133
packages/nicolium/src/queries/timelines/use-timelines.ts
Normal file
@ -0,0 +1,133 @@
|
||||
import { useClient } from '@/hooks/use-client';
|
||||
|
||||
import { queryKeys } from '../keys';
|
||||
|
||||
import { useTimeline } from './use-timeline';
|
||||
|
||||
import type {
|
||||
AntennaTimelineParams,
|
||||
BubbleTimelineParams,
|
||||
GroupTimelineParams,
|
||||
HashtagTimelineParams,
|
||||
HomeTimelineParams,
|
||||
LinkTimelineParams,
|
||||
ListTimelineParams,
|
||||
PaginationParams,
|
||||
PublicTimelineParams,
|
||||
WrenchedTimelineParams,
|
||||
} from 'pl-api';
|
||||
|
||||
const useHomeTimeline = (params?: Omit<HomeTimelineParams, keyof PaginationParams>) => {
|
||||
const client = useClient();
|
||||
const stream = 'home';
|
||||
|
||||
return useTimeline(
|
||||
queryKeys.timelines.home(params),
|
||||
(paginationParams) => client.timelines.homeTimeline({ ...params, ...paginationParams }),
|
||||
{ stream },
|
||||
);
|
||||
};
|
||||
|
||||
const usePublicTimeline = (params?: Omit<PublicTimelineParams, keyof PaginationParams>) => {
|
||||
const client = useClient();
|
||||
const stream = params?.local ? 'public:local' : params?.instance ? `public:remote` : 'public';
|
||||
|
||||
return useTimeline(
|
||||
queryKeys.timelines.public(params?.local, params),
|
||||
(paginationParams) => client.timelines.publicTimeline({ ...params, ...paginationParams }),
|
||||
{ stream },
|
||||
);
|
||||
};
|
||||
|
||||
const useHashtagTimeline = (
|
||||
hashtag: string,
|
||||
params?: Omit<HashtagTimelineParams, keyof PaginationParams>,
|
||||
) => {
|
||||
const client = useClient();
|
||||
|
||||
return useTimeline(
|
||||
queryKeys.timelines.hashtag(hashtag, params),
|
||||
(paginationParams) =>
|
||||
client.timelines.hashtagTimeline(hashtag, { ...params, ...paginationParams }),
|
||||
{ stream: 'hashtag', params: { tag: hashtag } },
|
||||
);
|
||||
};
|
||||
|
||||
const useLinkTimeline = (
|
||||
url: string,
|
||||
params?: Omit<LinkTimelineParams, keyof PaginationParams>,
|
||||
) => {
|
||||
const client = useClient();
|
||||
|
||||
return useTimeline(queryKeys.timelines.link(url, params), (paginationParams) =>
|
||||
client.timelines.linkTimeline(url, { ...params, ...paginationParams }),
|
||||
);
|
||||
};
|
||||
|
||||
const useListTimeline = (
|
||||
listId: string,
|
||||
params?: Omit<ListTimelineParams, keyof PaginationParams>,
|
||||
) => {
|
||||
const client = useClient();
|
||||
|
||||
return useTimeline(
|
||||
queryKeys.timelines.list(listId, params),
|
||||
(paginationParams) => client.timelines.listTimeline(listId, { ...params, ...paginationParams }),
|
||||
{ stream: 'list', params: { list: listId } },
|
||||
);
|
||||
};
|
||||
|
||||
const useGroupTimeline = (
|
||||
groupId: string,
|
||||
params?: Omit<GroupTimelineParams, keyof PaginationParams>,
|
||||
) => {
|
||||
const client = useClient();
|
||||
|
||||
return useTimeline(
|
||||
queryKeys.timelines.group(groupId, params),
|
||||
(paginationParams) =>
|
||||
client.timelines.groupTimeline(groupId, { ...params, ...paginationParams }),
|
||||
{ stream: 'group', params: { group: groupId } },
|
||||
);
|
||||
};
|
||||
|
||||
const useBubbleTimeline = (params?: Omit<BubbleTimelineParams, keyof PaginationParams>) => {
|
||||
const client = useClient();
|
||||
|
||||
return useTimeline(
|
||||
queryKeys.timelines.bubble(params),
|
||||
(paginationParams) => client.timelines.bubbleTimeline({ ...params, ...paginationParams }),
|
||||
{ stream: 'bubble' },
|
||||
);
|
||||
};
|
||||
|
||||
const useAntennaTimeline = (
|
||||
antennaId: string,
|
||||
params?: Omit<AntennaTimelineParams, keyof PaginationParams>,
|
||||
) => {
|
||||
const client = useClient();
|
||||
|
||||
return useTimeline(queryKeys.timelines.antenna(antennaId, params), (paginationParams) =>
|
||||
client.timelines.antennaTimeline(antennaId, { ...params, ...paginationParams }),
|
||||
);
|
||||
};
|
||||
|
||||
const useWrenchedTimeline = (params?: Omit<WrenchedTimelineParams, keyof PaginationParams>) => {
|
||||
const client = useClient();
|
||||
|
||||
return useTimeline(queryKeys.timelines.wrenched(params), (paginationParams) =>
|
||||
client.timelines.wrenchedTimeline({ ...params, ...paginationParams }),
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
useHomeTimeline,
|
||||
usePublicTimeline,
|
||||
useHashtagTimeline,
|
||||
useLinkTimeline,
|
||||
useListTimeline,
|
||||
useGroupTimeline,
|
||||
useBubbleTimeline,
|
||||
useAntennaTimeline,
|
||||
useWrenchedTimeline,
|
||||
};
|
||||
Reference in New Issue
Block a user