mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-15 18:30:39 +09:00
Speed up issue detail comments and refreshes
This commit is contained in:
parent
a4b05d8831
commit
9e8cd28f81
4 changed files with 276 additions and 77 deletions
|
|
@ -5,10 +5,12 @@ import {
|
|||
applyOptimisticIssueFieldUpdateToCollection,
|
||||
applyOptimisticIssueCommentUpdate,
|
||||
createOptimisticIssueComment,
|
||||
flattenIssueCommentPages,
|
||||
isQueuedIssueComment,
|
||||
matchesIssueRef,
|
||||
mergeIssueComments,
|
||||
upsertIssueComment,
|
||||
upsertIssueCommentInPages,
|
||||
} from "./optimistic-issue-comments";
|
||||
|
||||
describe("optimistic issue comments", () => {
|
||||
|
|
@ -128,6 +130,91 @@ describe("optimistic issue comments", () => {
|
|||
expect(next[0]?.body).toBe("Updated");
|
||||
});
|
||||
|
||||
it("flattens paged comments into one chronological thread", () => {
|
||||
const flattened = flattenIssueCommentPages([
|
||||
[
|
||||
{
|
||||
id: "comment-3",
|
||||
companyId: "company-1",
|
||||
issueId: "issue-1",
|
||||
authorAgentId: null,
|
||||
authorUserId: "board-1",
|
||||
body: "Newest",
|
||||
createdAt: new Date("2026-03-28T14:00:03.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:03.000Z"),
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: "comment-1",
|
||||
companyId: "company-1",
|
||||
issueId: "issue-1",
|
||||
authorAgentId: null,
|
||||
authorUserId: "board-1",
|
||||
body: "Oldest",
|
||||
createdAt: new Date("2026-03-28T14:00:01.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:01.000Z"),
|
||||
},
|
||||
{
|
||||
id: "comment-2",
|
||||
companyId: "company-1",
|
||||
issueId: "issue-1",
|
||||
authorAgentId: null,
|
||||
authorUserId: "board-1",
|
||||
body: "Middle",
|
||||
createdAt: new Date("2026-03-28T14:00:02.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:02.000Z"),
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
expect(flattened.map((comment) => comment.id)).toEqual(["comment-1", "comment-2", "comment-3"]);
|
||||
});
|
||||
|
||||
it("upserts paged comments without dropping older pages", () => {
|
||||
const nextPages = upsertIssueCommentInPages(
|
||||
[
|
||||
[
|
||||
{
|
||||
id: "comment-3",
|
||||
companyId: "company-1",
|
||||
issueId: "issue-1",
|
||||
authorAgentId: null,
|
||||
authorUserId: "board-1",
|
||||
body: "Newest",
|
||||
createdAt: new Date("2026-03-28T14:00:03.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:03.000Z"),
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: "comment-1",
|
||||
companyId: "company-1",
|
||||
issueId: "issue-1",
|
||||
authorAgentId: null,
|
||||
authorUserId: "board-1",
|
||||
body: "Oldest",
|
||||
createdAt: new Date("2026-03-28T14:00:01.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:01.000Z"),
|
||||
},
|
||||
],
|
||||
],
|
||||
{
|
||||
id: "comment-4",
|
||||
companyId: "company-1",
|
||||
issueId: "issue-1",
|
||||
authorAgentId: null,
|
||||
authorUserId: "board-1",
|
||||
body: "Brand new",
|
||||
createdAt: new Date("2026-03-28T14:00:04.000Z"),
|
||||
updatedAt: new Date("2026-03-28T14:00:04.000Z"),
|
||||
},
|
||||
);
|
||||
|
||||
expect(nextPages[0]?.map((comment) => comment.id)).toEqual(["comment-4", "comment-3"]);
|
||||
expect(nextPages[1]?.map((comment) => comment.id)).toEqual(["comment-1"]);
|
||||
});
|
||||
|
||||
it("applies optimistic reopen and reassignment updates to the issue cache", () => {
|
||||
const next = applyOptimisticIssueCommentUpdate(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ export function sortIssueComments<T extends { createdAt: Date | string; id: stri
|
|||
});
|
||||
}
|
||||
|
||||
function sortIssueCommentsDesc<T extends { createdAt: Date | string; id: string }>(comments: T[]) {
|
||||
return sortIssueComments(comments).reverse();
|
||||
}
|
||||
|
||||
export function createOptimisticIssueComment(params: {
|
||||
companyId: string;
|
||||
issueId: string;
|
||||
|
|
@ -92,6 +96,12 @@ export function mergeIssueComments(
|
|||
return sortIssueComments(merged);
|
||||
}
|
||||
|
||||
export function flattenIssueCommentPages(
|
||||
pages: ReadonlyArray<ReadonlyArray<IssueComment>> | undefined,
|
||||
): IssueComment[] {
|
||||
return sortIssueComments((pages ?? []).flatMap((page) => page));
|
||||
}
|
||||
|
||||
export function upsertIssueComment(
|
||||
comments: IssueComment[] | undefined,
|
||||
nextComment: IssueComment,
|
||||
|
|
@ -210,3 +220,24 @@ export function applyOptimisticIssueFieldUpdateToCollection(
|
|||
|
||||
return changed ? nextIssues : issues;
|
||||
}
|
||||
|
||||
export function upsertIssueCommentInPages(
|
||||
pages: ReadonlyArray<ReadonlyArray<IssueComment>> | undefined,
|
||||
nextComment: IssueComment,
|
||||
): IssueComment[][] {
|
||||
if (!pages || pages.length === 0) {
|
||||
return [[nextComment]];
|
||||
}
|
||||
|
||||
const nextPages = pages.map((page) => [...page]);
|
||||
for (let pageIndex = 0; pageIndex < nextPages.length; pageIndex += 1) {
|
||||
const existingIndex = nextPages[pageIndex]!.findIndex((comment) => comment.id === nextComment.id);
|
||||
if (existingIndex === -1) continue;
|
||||
nextPages[pageIndex]![existingIndex] = nextComment;
|
||||
nextPages[pageIndex] = sortIssueCommentsDesc(nextPages[pageIndex]!);
|
||||
return nextPages;
|
||||
}
|
||||
|
||||
nextPages[0] = sortIssueCommentsDesc([...nextPages[0]!, nextComment]);
|
||||
return nextPages;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue