From e755190baa43ef952fe3ef52a67c41594526797c Mon Sep 17 00:00:00 2001 From: Jinwoo-H Date: Sat, 30 May 2026 09:21:22 -0700 Subject: [PATCH] Fix assignee edit guard scoping Co-authored-by: Orca --- .../src/components/GitHubItemDialog.tsx | 19 ++++++++----------- .../src/components/PullRequestPage.tsx | 19 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/renderer/src/components/GitHubItemDialog.tsx b/src/renderer/src/components/GitHubItemDialog.tsx index 623f4afa2fd..6da641c7d18 100644 --- a/src/renderer/src/components/GitHubItemDialog.tsx +++ b/src/renderer/src/components/GitHubItemDialog.tsx @@ -4285,14 +4285,8 @@ function GHEditSection({ const [labelPopoverOpen, setLabelPopoverOpen] = useState(false) const [assigneePopoverOpen, setAssigneePopoverOpen] = useState(false) const [localAssignees, setLocalAssignees] = useState(assignees) - const hasEditedAssigneesRef = useRef(false) - const assigneesItemIdRef = useRef(item.id) - if (assigneesItemIdRef.current !== item.id) { - assigneesItemIdRef.current = item.id - // Why: item switches must clear the optimistic-edit guard before the - // assignee sync Effect runs, or the new item's assignees can render stale. - hasEditedAssigneesRef.current = false - } + const editedAssigneesItemKeyRef = useRef(null) + const assigneesItemKey = `${item.repoId}\0${item.id}` const patchWorkItem = useAppStore((s) => s.patchWorkItem) const patchProjectRowContent = useAppStore((s) => s.patchProjectRowContent) const { isPending, run } = useImmediateMutation() @@ -4333,11 +4327,11 @@ function GHEditSection({ // resolves with real data — but skip if the user already made an // optimistic edit so we don't clobber in-flight changes. useEffect(() => { - if (hasEditedAssigneesRef.current) { + if (editedAssigneesItemKeyRef.current === assigneesItemKey) { return } setLocalAssignees(assignees) - }, [item.id, assignees]) + }, [assigneesItemKey, assignees]) const handleStateChange = useCallback( (newState: 'open' | 'closed') => { @@ -4468,7 +4462,9 @@ function GHEditSection({ ? prevAssignees.filter((l) => l !== login) : [...prevAssignees, login] - hasEditedAssigneesRef.current = true + // Why: the optimistic guard is scoped to this repo item so switching + // items does not suppress the next item's assignee sync. + editedAssigneesItemKeyRef.current = assigneesItemKey if (isAssigned) { run('assignees', { mutate: () => @@ -4520,6 +4516,7 @@ function GHEditSection({ [ item.number, item.repoId, + assigneesItemKey, repoPath, projectOrigin, localAssignees, diff --git a/src/renderer/src/components/PullRequestPage.tsx b/src/renderer/src/components/PullRequestPage.tsx index 83e1cadf925..6deed821147 100644 --- a/src/renderer/src/components/PullRequestPage.tsx +++ b/src/renderer/src/components/PullRequestPage.tsx @@ -4396,14 +4396,8 @@ function GHEditSection({ const [labelPopoverOpen, setLabelPopoverOpen] = useState(false) const [assigneePopoverOpen, setAssigneePopoverOpen] = useState(false) const [localAssignees, setLocalAssignees] = useState(assignees) - const hasEditedAssigneesRef = useRef(false) - const assigneesItemIdRef = useRef(item.id) - if (assigneesItemIdRef.current !== item.id) { - assigneesItemIdRef.current = item.id - // Why: item switches must clear the optimistic-edit guard before the - // assignee sync Effect runs, or the new item's assignees can render stale. - hasEditedAssigneesRef.current = false - } + const editedAssigneesItemKeyRef = useRef(null) + const assigneesItemKey = `${item.repoId}\0${item.id}` const patchWorkItem = useAppStore((s) => s.patchWorkItem) const patchProjectRowContent = useAppStore((s) => s.patchProjectRowContent) const { isPending, run } = useImmediateMutation() @@ -4444,11 +4438,11 @@ function GHEditSection({ // resolves with real data — but skip if the user already made an // optimistic edit so we don't clobber in-flight changes. useEffect(() => { - if (hasEditedAssigneesRef.current) { + if (editedAssigneesItemKeyRef.current === assigneesItemKey) { return } setLocalAssignees(assignees) - }, [item.id, assignees]) + }, [assigneesItemKey, assignees]) const handleStateChange = useCallback( (newState: 'open' | 'closed') => { @@ -4579,7 +4573,9 @@ function GHEditSection({ ? prevAssignees.filter((l) => l !== login) : [...prevAssignees, login] - hasEditedAssigneesRef.current = true + // Why: the optimistic guard is scoped to this repo item so switching + // items does not suppress the next item's assignee sync. + editedAssigneesItemKeyRef.current = assigneesItemKey if (isAssigned) { run('assignees', { mutate: () => @@ -4631,6 +4627,7 @@ function GHEditSection({ [ item.number, item.repoId, + assigneesItemKey, repoPath, projectOrigin, localAssignees,