nicolium: add the old WIP timeline thing
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
163
packages/pl-fe/src/queries/timelines/use-home-timeline.ts
Normal file
163
packages/pl-fe/src/queries/timelines/use-home-timeline.ts
Normal file
@ -0,0 +1,163 @@
|
||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { importEntities } from '@/actions/importer';
|
||||
import { useTimelineStream } from '@/api/hooks/streaming/use-timeline-stream';
|
||||
import { useAppDispatch } from '@/hooks/use-app-dispatch';
|
||||
import { useClient } from '@/hooks/use-client';
|
||||
|
||||
import type { PaginatedResponse, Status } from 'pl-api';
|
||||
|
||||
type TimelineEntry =
|
||||
| {
|
||||
type: 'status';
|
||||
id: string;
|
||||
rebloggedBy: Array<string>;
|
||||
isConnectedTop?: boolean;
|
||||
isConnectedBottom?: boolean;
|
||||
}
|
||||
| {
|
||||
type: 'pending-status';
|
||||
id: string;
|
||||
}
|
||||
| {
|
||||
type: 'gap';
|
||||
}
|
||||
| {
|
||||
type: 'page-start';
|
||||
maxId?: string;
|
||||
}
|
||||
| {
|
||||
type: 'page-end';
|
||||
minId?: string;
|
||||
};
|
||||
|
||||
const processPage = ({ items: statuses, next }: PaginatedResponse<Status>) => {
|
||||
const timelinePage: Array<TimelineEntry> = [];
|
||||
|
||||
// if (previous) timelinePage.push({
|
||||
// type: 'page-start',
|
||||
// maxId: statuses.at(0)?.id,
|
||||
// });
|
||||
|
||||
const processStatus = (status: Status) => {
|
||||
if (timelinePage.some((entry) => entry.type === 'status' && entry.id === status.id))
|
||||
return false;
|
||||
|
||||
let isConnectedTop = false;
|
||||
const inReplyToId = (status.reblog || status).in_reply_to_id;
|
||||
|
||||
if (inReplyToId) {
|
||||
const foundStatus = statuses.find((status) => (status.reblog || status).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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status.reblog) {
|
||||
const existingEntry = timelinePage.find(
|
||||
(entry) => entry.type === 'status' && entry.id === status.reblog!.id,
|
||||
);
|
||||
|
||||
if (existingEntry?.type === 'status') {
|
||||
existingEntry.rebloggedBy.push(status.account.id);
|
||||
} else {
|
||||
timelinePage.push({
|
||||
type: 'status',
|
||||
id: status.reblog.id,
|
||||
rebloggedBy: [status.account.id],
|
||||
isConnectedTop,
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
timelinePage.push({
|
||||
type: 'status',
|
||||
id: status.id,
|
||||
rebloggedBy: [],
|
||||
isConnectedTop,
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const status of statuses) {
|
||||
processStatus(status);
|
||||
}
|
||||
|
||||
if (next)
|
||||
timelinePage.push({
|
||||
type: 'page-end',
|
||||
minId: statuses.at(-1)?.id,
|
||||
});
|
||||
|
||||
return timelinePage;
|
||||
};
|
||||
|
||||
const useHomeTimeline = () => {
|
||||
const client = useClient();
|
||||
const dispatch = useAppDispatch();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useTimelineStream('home');
|
||||
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
const queryKey = ['timelines', 'home'];
|
||||
|
||||
const query = useQuery({
|
||||
queryKey,
|
||||
queryFn: () => {
|
||||
setIsLoading(true);
|
||||
|
||||
return client.timelines
|
||||
.homeTimeline()
|
||||
.then((response) => {
|
||||
dispatch(importEntities({ statuses: response.items }));
|
||||
|
||||
return processPage(response);
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => setIsLoading(false));
|
||||
},
|
||||
});
|
||||
|
||||
const handleLoadMore = (entry: TimelineEntry) => {
|
||||
if (isLoading) return;
|
||||
|
||||
setIsLoading(true);
|
||||
if (entry.type !== 'page-end' && entry.type !== 'page-start') return;
|
||||
|
||||
return client.timelines
|
||||
.homeTimeline(entry.type === 'page-end' ? { max_id: entry.minId } : { min_id: entry.maxId })
|
||||
.then((response) => {
|
||||
dispatch(importEntities({ statuses: response.items }));
|
||||
|
||||
const timelinePage = processPage(response);
|
||||
|
||||
queryClient.setQueryData<Array<TimelineEntry>>(['timelines', 'home'], (oldData) => {
|
||||
if (!oldData) return timelinePage;
|
||||
|
||||
const index = oldData.indexOf(entry);
|
||||
return oldData.toSpliced(index, 1, ...timelinePage);
|
||||
});
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => setIsLoading(false));
|
||||
};
|
||||
return {
|
||||
...query,
|
||||
isLoading: isLoading,
|
||||
handleLoadMore,
|
||||
};
|
||||
};
|
||||
|
||||
export { useHomeTimeline, type TimelineEntry };
|
||||
Reference in New Issue
Block a user