aa6023e421
* feat(web): per-task color indicator in sidebar (localStorage) Adds a color marker to tasks in the left task list for better visual identification. Right-click a task → Color submenu picks one of seven colors (or None to clear). Color renders as a 3px bar on the left edge of the row and persists in localStorage. * fix(web): address review feedback on task color indicator - Filter storage events to TASK_COLORS_STORAGE_KEY so cross-tab writes to unrelated keys no longer wake every TaskItem subscriber. - Cache the parsed color map at module level, invalidated on writes and cross-tab storage events. Avoids re-parsing localStorage on every getSnapshot call (N tasks × N renders). - Drop redundant typeof string check on Object.entries keys. - Dim the colored bar to opacity-60 when not selected so colored rows remain distinguishable from colored-and-selected rows at the left edge. * refactor(web): remove test-only export from task-colors Drop the __resetTaskColorsCacheForTests escape hatch from the production module. Tests now invalidate the cache via a synthesized cross-tab `storage` event, exercising the same listener path the production code uses for real cross-tab updates. --------- Co-authored-by: Kandev Agent <agent@kandev.dev>
35 lines
1.1 KiB
TypeScript
35 lines
1.1 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useSyncExternalStore } from "react";
|
|
import {
|
|
getTaskColor,
|
|
setTaskColor,
|
|
TASK_COLORS_CHANGED_EVENT,
|
|
TASK_COLORS_STORAGE_KEY,
|
|
type TaskColor,
|
|
} from "@/lib/task-colors";
|
|
|
|
function subscribe(cb: () => void): () => void {
|
|
if (typeof window === "undefined") return () => {};
|
|
const onStorage = (e: StorageEvent) => {
|
|
if (e.key === null || e.key === TASK_COLORS_STORAGE_KEY) cb();
|
|
};
|
|
window.addEventListener(TASK_COLORS_CHANGED_EVENT, cb);
|
|
window.addEventListener("storage", onStorage);
|
|
return () => {
|
|
window.removeEventListener(TASK_COLORS_CHANGED_EVENT, cb);
|
|
window.removeEventListener("storage", onStorage);
|
|
};
|
|
}
|
|
|
|
const getServerSnapshot = (): TaskColor | null => null;
|
|
|
|
export function useTaskColor(taskId: string | undefined): TaskColor | null {
|
|
const getSnapshot = useCallback(() => (taskId ? getTaskColor(taskId) : null), [taskId]);
|
|
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
}
|
|
|
|
export function useSetTaskColor(): (taskId: string, color: TaskColor | null) => void {
|
|
return useCallback((taskId, color) => setTaskColor(taskId, color), []);
|
|
}
|