refactor: update recordings handling with pagination support and improve infinite scroll functionality

This commit is contained in:
asepharyana
2026-08-26 17:05:41 +07:00
parent b640079cb6
commit 0c3a82ad77
4 changed files with 255 additions and 110 deletions
+1
View File
@@ -48,6 +48,7 @@ export {
} from "./use-moderation";
export {
useDeleteRecording,
useLoadMoreRecordings,
useRecordings,
useRecordingsWsSync,
} from "./use-recordings";
+69 -10
View File
@@ -2,27 +2,79 @@ import { useEffect } from "react";
import useSWR, { useSWRConfig } from "swr";
import { useAction } from "@/hooks/use-action";
import { recordingsApi } from "@/lib/api";
import type { VoiceRecording } from "@/lib/types";
import type { PaginatedRecordings, VoiceRecording } from "@/lib/types";
import type { WsHook } from "@/lib/ws-hook";
const RECORDINGS_KEY = ["recordings"] as const;
export function useRecordings(initialData?: VoiceRecording[]) {
return useSWR<VoiceRecording[]>(
export function useRecordingsPage(initialPage?: PaginatedRecordings) {
return useSWR<PaginatedRecordings>(
RECORDINGS_KEY,
async () => {
const res = await recordingsApi.list(50);
return res.items;
() => recordingsApi.list(50),
{ fallbackData: initialPage },
);
}
export function useRecordings(initialPage?: PaginatedRecordings) {
const page = useRecordingsPage(initialPage);
return {
...page,
data: page.data?.items,
nextCursor: page.data?.nextCursor ?? null,
hasMore: page.data?.hasMore ?? false,
refetch: () => page.mutate(),
};
}
export function useLoadMoreRecordings() {
const { mutate } = useSWRConfig();
return useAction(
async ({
channelId,
userId,
cursor,
}: {
channelId?: string;
userId?: string;
cursor: string;
}) => {
const result = await recordingsApi.list(50, channelId, userId, cursor);
await mutate(
RECORDINGS_KEY,
(old: PaginatedRecordings | undefined): PaginatedRecordings => {
if (!old) return result;
const existingIds = new Set(old.items.map((r) => r.id));
const newUnique = result.items.filter((r) => !existingIds.has(r.id));
return {
items: [...old.items, ...newUnique],
nextCursor: result.nextCursor,
hasMore: result.hasMore,
};
},
{ revalidate: false },
);
return result;
},
{ fallbackData: initialData },
);
}
export function useDeleteRecording() {
const { mutate } = useSWRConfig();
return useAction((id: string) => recordingsApi.delete(id), {
onSuccess: () => {
void mutate(RECORDINGS_KEY);
onSuccess: (_, id) => {
void mutate(
RECORDINGS_KEY,
(
old: PaginatedRecordings | undefined,
): PaginatedRecordings | undefined => {
if (!old) return old;
return {
...old,
items: old.items.filter((r) => r.id !== id),
};
},
{ revalidate: false },
);
},
});
}
@@ -34,7 +86,14 @@ export function useRecordingsWsSync(ws: WsHook) {
const rec = data as VoiceRecording;
void mutate(
RECORDINGS_KEY,
(old: VoiceRecording[] | undefined) => (old ? [rec, ...old] : [rec]),
(old: PaginatedRecordings | undefined): PaginatedRecordings => {
if (!old) return { items: [rec], nextCursor: null, hasMore: false };
if (old.items.some((r) => r.id === rec.id)) return old;
return {
...old,
items: [rec, ...old.items],
};
},
{ revalidate: false },
);
});