fix(ui): avoid freeze on notification click
This commit is contained in:
parent
62b381b844
commit
1b751ac16d
2 changed files with 97 additions and 31 deletions
|
|
@ -142,19 +142,19 @@ export default function NotificationCenter() {
|
|||
}, [filter, notifications]);
|
||||
|
||||
async function handleOpenNotification(notification: OrchaiNotification) {
|
||||
if (!notification.read) {
|
||||
try {
|
||||
await markNotificationRead(notification.id);
|
||||
setNotifications((prev) =>
|
||||
prev.map((n) => (n.id === notification.id ? { ...n, read: true } : n))
|
||||
);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
setOpen(false);
|
||||
|
||||
if (!notification.read) {
|
||||
setNotifications((prev) =>
|
||||
prev.map((n) => (n.id === notification.id ? { ...n, read: true } : n))
|
||||
);
|
||||
|
||||
// Do not block navigation on read acknowledgement.
|
||||
void markNotificationRead(notification.id).catch(() => {
|
||||
// ignore
|
||||
});
|
||||
}
|
||||
|
||||
if (notification.ticket_id) {
|
||||
navigate(`/tickets/${notification.ticket_id}`);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -58,8 +58,11 @@ export default function TicketDetail() {
|
|||
const [targetBranch, setTargetBranch] = useState("");
|
||||
const [availableBranches, setAvailableBranches] = useState<string[]>([]);
|
||||
const [branchInputMode, setBranchInputMode] = useState<"select" | "manual">("select");
|
||||
const [branchesLoadedForWorktreeId, setBranchesLoadedForWorktreeId] = useState<string | null>(null);
|
||||
const [branchesLoading, setBranchesLoading] = useState(false);
|
||||
const [branchesError, setBranchesError] = useState("");
|
||||
const [diffLoading, setDiffLoading] = useState(false);
|
||||
const [diffError, setDiffError] = useState("");
|
||||
const [tab, setTab] = useState<"info" | "analyst" | "developer" | "diff">("info");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
|
@ -93,29 +96,24 @@ export default function TicketDetail() {
|
|||
|
||||
async function loadData() {
|
||||
if (!ticketId) return;
|
||||
setError("");
|
||||
try {
|
||||
const result = await getTicketResult(ticketId);
|
||||
setTicket(result.ticket);
|
||||
setWorktree(result.worktree);
|
||||
|
||||
if (result.ticket.developer_report) setTab("developer");
|
||||
else if (result.ticket.analyst_report) setTab("analyst");
|
||||
setTab("info");
|
||||
setDiff(null);
|
||||
setDiffError("");
|
||||
setDiffLoading(false);
|
||||
setAvailableBranches([]);
|
||||
setBranchInputMode("select");
|
||||
setTargetBranch("");
|
||||
setBranchesError("");
|
||||
setBranchesLoading(false);
|
||||
setBranchesLoadedForWorktreeId(null);
|
||||
|
||||
if (result.worktree && result.worktree.status === "Active") {
|
||||
await loadBranchOptions(result.worktree.id);
|
||||
try {
|
||||
const d = await getWorktreeDiff(result.worktree.id);
|
||||
setDiff(d);
|
||||
} catch {
|
||||
setDiff(null);
|
||||
}
|
||||
} else {
|
||||
setDiff(null);
|
||||
setAvailableBranches([]);
|
||||
setBranchInputMode("select");
|
||||
setTargetBranch("");
|
||||
setBranchesError("");
|
||||
setBranchesLoading(false);
|
||||
return;
|
||||
}
|
||||
} catch (err) {
|
||||
setError(getErrorMessage(err));
|
||||
|
|
@ -123,9 +121,61 @@ export default function TicketDetail() {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
loadData();
|
||||
void loadData();
|
||||
}, [ticketId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tab !== "info") return;
|
||||
if (!worktree || worktree.status !== "Active") return;
|
||||
if (branchesLoading) return;
|
||||
if (branchesLoadedForWorktreeId === worktree.id) return;
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
void (async () => {
|
||||
await loadBranchOptions(worktree.id);
|
||||
if (!cancelled) {
|
||||
setBranchesLoadedForWorktreeId(worktree.id);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [tab, worktree?.id, worktree?.status, branchesLoading, branchesLoadedForWorktreeId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tab !== "diff") return;
|
||||
if (!worktree || worktree.status !== "Active") return;
|
||||
if (diff !== null || diffLoading) return;
|
||||
|
||||
let cancelled = false;
|
||||
setDiffLoading(true);
|
||||
setDiffError("");
|
||||
|
||||
void getWorktreeDiff(worktree.id)
|
||||
.then((value) => {
|
||||
if (!cancelled) {
|
||||
setDiff(value);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!cancelled) {
|
||||
setDiff("");
|
||||
setDiffError(getErrorMessage(err));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) {
|
||||
setDiffLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [tab, worktree?.id, worktree?.status, diff, diffLoading]);
|
||||
|
||||
async function handleRetry() {
|
||||
if (!ticketId) return;
|
||||
setLoading(true);
|
||||
|
|
@ -214,7 +264,11 @@ export default function TicketDetail() {
|
|||
label: "Developer Report",
|
||||
disabled: !ticket.developer_report,
|
||||
},
|
||||
{ key: "diff" as const, label: "Diff", disabled: !diff && !worktree },
|
||||
{
|
||||
key: "diff" as const,
|
||||
label: "Diff",
|
||||
disabled: !worktree || worktree.status !== "Active",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
|
|
@ -420,7 +474,19 @@ export default function TicketDetail() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{tab === "diff" && <DiffViewer diff={diff || ""} />}
|
||||
{tab === "diff" && (
|
||||
<>
|
||||
{diffLoading ? (
|
||||
<div className="p-4 text-sm text-gray-500">Loading diff...</div>
|
||||
) : diffError ? (
|
||||
<div className={noticeClass("warning")}>
|
||||
Could not load diff: {diffError}
|
||||
</div>
|
||||
) : (
|
||||
<DiffViewer diff={diff || ""} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={isDeleteWorktreeModalOpen}
|
||||
|
|
|
|||
Loading…
Reference in a new issue