mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 17:46:06 +00:00
feat(comments): implement pagination for comments retrieval with limit and offset
This commit is contained in:
@@ -83,15 +83,23 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
|
|||||||
|
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const includeResolved = searchParams.get('includeResolved') !== 'false';
|
const includeResolved = searchParams.get('includeResolved') !== 'false';
|
||||||
|
const limit = Math.min(Math.max(1, parseInt(searchParams.get('limit') ?? '200', 10)), 500);
|
||||||
|
const offset = Math.max(0, parseInt(searchParams.get('offset') ?? '0', 10));
|
||||||
|
|
||||||
const commentsRevision = await db.comment.aggregate({
|
const commentsFilter = {
|
||||||
where: {
|
versionId,
|
||||||
versionId,
|
parentId: null as null,
|
||||||
...(includeResolved ? {} : { isResolved: false }),
|
...(includeResolved ? {} : { isResolved: false }),
|
||||||
},
|
};
|
||||||
_count: { id: true },
|
|
||||||
_max: { updatedAt: true },
|
const [commentsRevision, total] = await Promise.all([
|
||||||
});
|
db.comment.aggregate({
|
||||||
|
where: { versionId, ...(includeResolved ? {} : { isResolved: false }) },
|
||||||
|
_count: { id: true },
|
||||||
|
_max: { updatedAt: true },
|
||||||
|
}),
|
||||||
|
db.comment.count({ where: commentsFilter }),
|
||||||
|
]);
|
||||||
|
|
||||||
const etag = `"comments:${versionId}:${includeResolved ? 1 : 0}:${commentsRevision._count.id}:${commentsRevision._max.updatedAt?.getTime() ?? 0}"`;
|
const etag = `"comments:${versionId}:${includeResolved ? 1 : 0}:${commentsRevision._count.id}:${commentsRevision._max.updatedAt?.getTime() ?? 0}"`;
|
||||||
const ifNoneMatch = request.headers.get('if-none-match');
|
const ifNoneMatch = request.headers.get('if-none-match');
|
||||||
@@ -109,12 +117,10 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const comments = await db.comment.findMany({
|
const comments = await db.comment.findMany({
|
||||||
where: {
|
where: commentsFilter,
|
||||||
versionId,
|
|
||||||
parentId: null, // Only top-level comments
|
|
||||||
...(includeResolved ? {} : { isResolved: false }),
|
|
||||||
},
|
|
||||||
orderBy: { timestamp: 'asc' },
|
orderBy: { timestamp: 'asc' },
|
||||||
|
skip: offset,
|
||||||
|
take: limit,
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
content: true,
|
content: true,
|
||||||
@@ -162,7 +168,13 @@ export async function GET(request: NextRequest, { params }: RouteParams) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = successResponse({ comments });
|
const response = successResponse({
|
||||||
|
comments,
|
||||||
|
total,
|
||||||
|
hasMore: offset + comments.length < total,
|
||||||
|
offset,
|
||||||
|
limit,
|
||||||
|
});
|
||||||
response.headers.set('ETag', etag);
|
response.headers.set('ETag', etag);
|
||||||
return withCacheControl(response, 'private, no-cache');
|
return withCacheControl(response, 'private, no-cache');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -38,20 +38,38 @@ export function useVideoPageData({
|
|||||||
if (etag) headers['If-None-Match'] = etag;
|
if (etag) headers['If-None-Match'] = etag;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetch(`/api/versions/${versionId}/comments?includeResolved=true`, {
|
const LIMIT = 200;
|
||||||
cache: 'no-store',
|
let offset = 0;
|
||||||
headers,
|
let allComments: Comment[] = [];
|
||||||
});
|
let latestEtag: string | null = null;
|
||||||
|
|
||||||
if (res.status === 304) return;
|
// Fetch pages until we have all comments
|
||||||
if (!res.ok) return;
|
while (true) {
|
||||||
|
const res = await fetch(
|
||||||
|
`/api/versions/${versionId}/comments?includeResolved=true&limit=${LIMIT}&offset=${offset}`,
|
||||||
|
{ cache: 'no-store', headers: offset === 0 ? headers : {} }
|
||||||
|
);
|
||||||
|
|
||||||
const etag = res.headers.get('etag');
|
if (offset === 0 && res.status === 304) return;
|
||||||
if (etag) commentsEtagRef.current.set(versionId, etag);
|
if (!res.ok) return;
|
||||||
|
|
||||||
const payload = await res.json();
|
if (offset === 0) {
|
||||||
const commentsList = payload?.data?.comments;
|
latestEtag = res.headers.get('etag');
|
||||||
if (!Array.isArray(commentsList)) return;
|
}
|
||||||
|
|
||||||
|
const payload = await res.json();
|
||||||
|
const page: Comment[] = payload?.data?.comments;
|
||||||
|
if (!Array.isArray(page)) return;
|
||||||
|
|
||||||
|
allComments = allComments.concat(page);
|
||||||
|
|
||||||
|
if (!payload?.data?.hasMore) break;
|
||||||
|
offset += LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestEtag) commentsEtagRef.current.set(versionId, latestEtag);
|
||||||
|
|
||||||
|
const commentsList = allComments;
|
||||||
|
|
||||||
setVideo((prev) => {
|
setVideo((prev) => {
|
||||||
if (!prev) return prev;
|
if (!prev) return prev;
|
||||||
|
|||||||
Reference in New Issue
Block a user