Skip to content
Source
Sign inSign up

thread-undo-notice.ts

56 lines1.4 KB

1import * as ThreadUndo from "@t3tools/client-runtime/state/thread-undo";
2import { useSyncExternalStore } from "react";
3​
4type UndoNotice = {
5 readonly action: "Settled" | "Snoozed" | "Unpinned";
6 readonly claim: ReturnType<typeof ThreadUndo.begin>;
7 readonly undo: () => Promise<void>;
8};
9​
10let notice: UndoNotice | null = null;
11let expiry: ReturnType<typeof setTimeout> | undefined;
12const listeners = new Set<() => void>();
13​
14function notify() {
15 for (const listener of listeners) listener();
17​
18export function readThreadUndoNotice() {
19 return notice;
21​
22export function dismissThreadUndoNotice() {
23 const previous = notice;
24 notice = null;
25 clearTimeout(expiry);
26 previous?.claim.finish();
27 notify();
29​
30ThreadUndo.subscribe(() => {
31 if (notice !== null && !notice.claim.isCurrent()) dismissThreadUndoNotice();
32});
33​
34export function showThreadUndoNotice(options: UndoNotice) {
35 if (!options.claim.isCurrent()) return;
36 dismissThreadUndoNotice();
37 notice = {
38 ...options,
39 undo: async () => {
40 if (notice?.claim !== options.claim || !options.claim.isCurrent()) return;
41 dismissThreadUndoNotice();
42 await options.undo();
43 },
44 };
45 notify();
46 expiry = setTimeout(dismissThreadUndoNotice, 5_000);
48​
49export function useThreadUndoNotice() {
50 return useSyncExternalStore((listener) => {
51 listeners.add(listener);
52 return () => {
53 listeners.delete(listener);
54 };
55 }, readThreadUndoNotice);