mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 19:20:39 +09:00
fix(ui): harden issue comment editor sync
This commit is contained in:
parent
996c7eb727
commit
327eadb45c
4 changed files with 59 additions and 2 deletions
|
|
@ -364,6 +364,19 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
|
||||||
return map;
|
return map;
|
||||||
}, [mentions]);
|
}, [mentions]);
|
||||||
|
|
||||||
|
const setEditorRef = useCallback((instance: MDXEditorMethods | null) => {
|
||||||
|
ref.current = instance;
|
||||||
|
if (!instance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (valueRef.current !== latestValueRef.current) {
|
||||||
|
// Re-apply the latest controlled value once MDXEditor exposes its imperative API.
|
||||||
|
echoIgnoreMarkdownRef.current = valueRef.current;
|
||||||
|
instance.setMarkdown(valueRef.current);
|
||||||
|
latestValueRef.current = valueRef.current;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const filteredMentions = useMemo<AutocompleteOption[]>(() => {
|
const filteredMentions = useMemo<AutocompleteOption[]>(() => {
|
||||||
if (!mentionState) return [];
|
if (!mentionState) return [];
|
||||||
const q = mentionState.query.trim().toLowerCase();
|
const q = mentionState.query.trim().toLowerCase();
|
||||||
|
|
@ -798,7 +811,7 @@ export const MarkdownEditor = forwardRef<MarkdownEditorRef, MarkdownEditorProps>
|
||||||
onPasteCapture={handlePasteCapture}
|
onPasteCapture={handlePasteCapture}
|
||||||
>
|
>
|
||||||
<MDXEditor
|
<MDXEditor
|
||||||
ref={ref}
|
ref={setEditorRef}
|
||||||
markdown={value}
|
markdown={value}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={(next) => {
|
onChange={(next) => {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {
|
||||||
applyOptimisticIssueCommentUpdate,
|
applyOptimisticIssueCommentUpdate,
|
||||||
createOptimisticIssueComment,
|
createOptimisticIssueComment,
|
||||||
flattenIssueCommentPages,
|
flattenIssueCommentPages,
|
||||||
|
getNextIssueCommentPageParam,
|
||||||
isQueuedIssueComment,
|
isQueuedIssueComment,
|
||||||
matchesIssueRef,
|
matchesIssueRef,
|
||||||
mergeIssueComments,
|
mergeIssueComments,
|
||||||
|
|
@ -171,6 +172,40 @@ describe("optimistic issue comments", () => {
|
||||||
expect(flattened.map((comment) => comment.id)).toEqual(["comment-1", "comment-2", "comment-3"]);
|
expect(flattened.map((comment) => comment.id)).toEqual(["comment-1", "comment-2", "comment-3"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns no next page param when the last page is missing", () => {
|
||||||
|
expect(getNextIssueCommentPageParam(undefined, 50)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the oldest id when the last page is full", () => {
|
||||||
|
expect(
|
||||||
|
getNextIssueCommentPageParam(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: "comment-2",
|
||||||
|
companyId: "company-1",
|
||||||
|
issueId: "issue-1",
|
||||||
|
authorAgentId: null,
|
||||||
|
authorUserId: "board-1",
|
||||||
|
body: "Second",
|
||||||
|
createdAt: new Date("2026-03-28T14:00:02.000Z"),
|
||||||
|
updatedAt: new Date("2026-03-28T14:00:02.000Z"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "comment-1",
|
||||||
|
companyId: "company-1",
|
||||||
|
issueId: "issue-1",
|
||||||
|
authorAgentId: null,
|
||||||
|
authorUserId: "board-1",
|
||||||
|
body: "First",
|
||||||
|
createdAt: new Date("2026-03-28T14:00:01.000Z"),
|
||||||
|
updatedAt: new Date("2026-03-28T14:00:01.000Z"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
).toBe("comment-1");
|
||||||
|
});
|
||||||
|
|
||||||
it("upserts paged comments without dropping older pages", () => {
|
it("upserts paged comments without dropping older pages", () => {
|
||||||
const nextPages = upsertIssueCommentInPages(
|
const nextPages = upsertIssueCommentInPages(
|
||||||
[
|
[
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,14 @@ export function flattenIssueCommentPages(
|
||||||
return sortIssueComments((pages ?? []).flatMap((page) => page));
|
return sortIssueComments((pages ?? []).flatMap((page) => page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getNextIssueCommentPageParam(
|
||||||
|
lastPage: ReadonlyArray<IssueComment> | undefined,
|
||||||
|
pageSize: number,
|
||||||
|
): string | undefined {
|
||||||
|
if (!lastPage || lastPage.length < pageSize) return undefined;
|
||||||
|
return lastPage[lastPage.length - 1]?.id;
|
||||||
|
}
|
||||||
|
|
||||||
export function upsertIssueComment(
|
export function upsertIssueComment(
|
||||||
comments: IssueComment[] | undefined,
|
comments: IssueComment[] | undefined,
|
||||||
nextComment: IssueComment,
|
nextComment: IssueComment,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import {
|
||||||
applyOptimisticIssueCommentUpdate,
|
applyOptimisticIssueCommentUpdate,
|
||||||
createOptimisticIssueComment,
|
createOptimisticIssueComment,
|
||||||
flattenIssueCommentPages,
|
flattenIssueCommentPages,
|
||||||
|
getNextIssueCommentPageParam,
|
||||||
isQueuedIssueComment,
|
isQueuedIssueComment,
|
||||||
matchesIssueRef,
|
matchesIssueRef,
|
||||||
mergeIssueComments,
|
mergeIssueComments,
|
||||||
|
|
@ -416,7 +417,7 @@ export function IssueDetail() {
|
||||||
enabled: !!issueId,
|
enabled: !!issueId,
|
||||||
initialPageParam: null as string | null,
|
initialPageParam: null as string | null,
|
||||||
getNextPageParam: (lastPage) =>
|
getNextPageParam: (lastPage) =>
|
||||||
lastPage.length === ISSUE_COMMENT_PAGE_SIZE ? lastPage[lastPage.length - 1]?.id : undefined,
|
getNextIssueCommentPageParam(lastPage, ISSUE_COMMENT_PAGE_SIZE),
|
||||||
placeholderData: keepPreviousData,
|
placeholderData: keepPreviousData,
|
||||||
});
|
});
|
||||||
const comments = useMemo(
|
const comments = useMemo(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue