Files
kandev/apps/web/hooks/domains/github/use-issue-watches.ts
T
2026-06-15 18:54:16 +01:00

114 lines
3.6 KiB
TypeScript

"use client";
import { useEffect, useCallback } from "react";
import {
listIssueWatches,
createIssueWatch,
updateIssueWatch,
deleteIssueWatch,
triggerIssueWatch,
triggerAllIssueWatches,
previewResetIssueWatch,
resetIssueWatch,
} from "@/lib/api/domains/github-api";
import { useAppStore, useAppStoreApi } from "@/components/state-provider";
import type { CreateIssueWatchRequest, UpdateIssueWatchRequest } from "@/lib/types/github";
// useIssueWatches has three modes:
// - workspaceId: string → fetch watches scoped to one workspace
// - workspaceId: undefined → fetch watches across all workspaces
// - workspaceId: null → don't fetch (caller hasn't resolved a workspace yet)
export function useIssueWatches(workspaceId?: string | null) {
const items = useAppStore((state) => state.issueWatches.items);
const loaded = useAppStore((state) => state.issueWatches.loaded);
const loading = useAppStore((state) => state.issueWatches.loading);
const setIssueWatches = useAppStore((state) => state.setIssueWatches);
const setIssueWatchesLoading = useAppStore((state) => state.setIssueWatchesLoading);
const addWatch = useAppStore((state) => state.addIssueWatch);
const updateWatch = useAppStore((state) => state.updateIssueWatch);
const removeWatch = useAppStore((state) => state.removeIssueWatch);
// storeApi exposes getState() without subscribing — used in reset() to
// read the current watch row outside of the React render cycle so the
// callback doesn't need `items` as a dependency.
const storeApi = useAppStoreApi();
useEffect(() => {
if (workspaceId === null || loaded || loading) return;
setIssueWatchesLoading(true);
listIssueWatches(workspaceId ?? undefined, { cache: "no-store" })
.then((response) => {
setIssueWatches(response?.watches ?? []);
})
.catch(() => {
setIssueWatches([]);
})
.finally(() => {
setIssueWatchesLoading(false);
});
}, [workspaceId, loaded, loading, setIssueWatches, setIssueWatchesLoading]);
const create = useCallback(
async (req: CreateIssueWatchRequest) => {
const watch = await createIssueWatch(req);
addWatch(watch);
return watch;
},
[addWatch],
);
const update = useCallback(
async (id: string, req: UpdateIssueWatchRequest) => {
const watch = await updateIssueWatch(id, req);
updateWatch(watch);
return watch;
},
[updateWatch],
);
const remove = useCallback(
async (id: string) => {
await deleteIssueWatch(id);
removeWatch(id);
},
[removeWatch],
);
const trigger = useCallback(async (id: string) => {
return triggerIssueWatch(id);
}, []);
const triggerAll = useCallback(async () => {
if (!workspaceId) return null;
return triggerAllIssueWatches(workspaceId);
}, [workspaceId]);
const previewReset = useCallback(async (id: string, watchWorkspaceId: string) => {
return previewResetIssueWatch(id, watchWorkspaceId);
}, []);
const reset = useCallback(
async (id: string, watchWorkspaceId: string) => {
const res = await resetIssueWatch(id, watchWorkspaceId);
// Patch the cached watch so the "Last polled" column reflects the
// reset immediately without waiting for the next poll tick.
const current = storeApi.getState().issueWatches.items.find((w) => w.id === id);
if (current) updateWatch({ ...current, last_polled_at: null });
return res;
},
[storeApi, updateWatch],
);
return {
items,
loaded,
loading,
create,
update,
remove,
trigger,
triggerAll,
previewReset,
reset,
};
}