import type { QueryClient } from "@tanstack/react-query"; import { issueKeys } from "./queries"; import type { Issue } from "../types"; import type { ListIssuesResponse } from "../types"; export function onIssueCreated( qc: QueryClient, wsId: string, issue: Issue, ) { qc.setQueryData(issueKeys.list(wsId), (old) => { if (!old || old.issues.some((i) => i.id === issue.id)) return old; return { ...old, issues: [...old.issues, issue], total: old.total + 1, doneTotal: (old.doneTotal ?? 0) + (issue.status === "done" ? 1 : 0), }; }); qc.invalidateQueries({ queryKey: issueKeys.myAll(wsId) }); if (issue.parent_issue_id) { qc.invalidateQueries({ queryKey: issueKeys.children(wsId, issue.parent_issue_id) }); qc.invalidateQueries({ queryKey: issueKeys.childProgress(wsId) }); } } export function onIssueUpdated( qc: QueryClient, wsId: string, issue: Partial & { id: string }, ) { // Look up the OLD parent before mutating list state, so we can keep // the parent's children cache in sync (powers the sub-issues list // shown on the parent issue page). const listData = qc.getQueryData(issueKeys.list(wsId)); const detailData = qc.getQueryData(issueKeys.detail(wsId, issue.id)); const oldParentId = detailData?.parent_issue_id ?? listData?.issues.find((i) => i.id === issue.id)?.parent_issue_id ?? null; // The NEW parent comes from the WS payload when parent_issue_id changed const newParentId = issue.parent_issue_id ?? null; const parentChanged = issue.parent_issue_id !== undefined && newParentId !== oldParentId; qc.setQueryData(issueKeys.list(wsId), (old) => { if (!old) return old; const prev = old.issues.find((i) => i.id === issue.id); const wasDone = prev?.status === "done"; const isDone = issue.status === "done"; // Only adjust doneTotal when status field is present and actually changed let doneDelta = 0; if (issue.status !== undefined) { if (!wasDone && isDone) doneDelta = 1; else if (wasDone && !isDone) doneDelta = -1; } return { ...old, issues: old.issues.map((i) => i.id === issue.id ? { ...i, ...issue } : i, ), doneTotal: (old.doneTotal ?? 0) + doneDelta, }; }); qc.invalidateQueries({ queryKey: issueKeys.myAll(wsId) }); qc.setQueryData(issueKeys.detail(wsId, issue.id), (old) => old ? { ...old, ...issue } : old, ); // Invalidate old parent's children (issue was removed from it) if (oldParentId) { if (parentChanged) { qc.invalidateQueries({ queryKey: issueKeys.children(wsId, oldParentId) }); } else { qc.setQueryData(issueKeys.children(wsId, oldParentId), (old) => old?.map((c) => (c.id === issue.id ? { ...c, ...issue } : c)), ); } } // Invalidate new parent's children (issue was added to it) if (newParentId && parentChanged) { qc.invalidateQueries({ queryKey: issueKeys.children(wsId, newParentId) }); } if (oldParentId || newParentId) { if (issue.status !== undefined || issue.parent_issue_id !== undefined) { qc.invalidateQueries({ queryKey: issueKeys.childProgress(wsId) }); } } } export function onIssueDeleted( qc: QueryClient, wsId: string, issueId: string, ) { // Look up the issue before removing it to check for parent_issue_id const listData = qc.getQueryData(issueKeys.list(wsId)); const deleted = listData?.issues.find((i) => i.id === issueId); qc.setQueryData(issueKeys.list(wsId), (old) => { if (!old) return old; const del = old.issues.find((i) => i.id === issueId); return { ...old, issues: old.issues.filter((i) => i.id !== issueId), total: old.total - 1, doneTotal: (old.doneTotal ?? 0) - (del?.status === "done" ? 1 : 0), }; }); qc.invalidateQueries({ queryKey: issueKeys.myAll(wsId) }); qc.removeQueries({ queryKey: issueKeys.detail(wsId, issueId) }); qc.removeQueries({ queryKey: issueKeys.timeline(issueId) }); qc.removeQueries({ queryKey: issueKeys.reactions(issueId) }); qc.removeQueries({ queryKey: issueKeys.subscribers(issueId) }); qc.removeQueries({ queryKey: issueKeys.children(wsId, issueId) }); if (deleted?.parent_issue_id) { qc.invalidateQueries({ queryKey: issueKeys.children(wsId, deleted.parent_issue_id) }); qc.invalidateQueries({ queryKey: issueKeys.childProgress(wsId) }); } }