feat: add localStorage persistence to focus store
Wraps the focus store with zustand/middleware persist to maintain queue state across page refreshes and app restarts. Persists: - current: The currently focused item - queue: Remaining items in the work queue Not persisted (transient state): - isLoading: Reset on mount - error: Reset on mount Storage key: "mc-focus-store" This prevents losing your place in the queue when the app restarts or the page refreshes during development. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
|
import { persist } from "zustand/middleware";
|
||||||
import type { FocusAction, FocusItem } from "@/lib/types";
|
import type { FocusAction, FocusItem } from "@/lib/types";
|
||||||
|
|
||||||
export interface FocusState {
|
export interface FocusState {
|
||||||
@@ -34,72 +35,83 @@ export interface FocusState {
|
|||||||
setError: (error: string | null) => void;
|
setError: (error: string | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useFocusStore = create<FocusState>((set, get) => ({
|
export const useFocusStore = create<FocusState>()(
|
||||||
current: null,
|
persist(
|
||||||
queue: [],
|
(set, get) => ({
|
||||||
isLoading: false,
|
current: null,
|
||||||
error: null,
|
queue: [],
|
||||||
|
|
||||||
setItems: (items) => {
|
|
||||||
const [first, ...rest] = items;
|
|
||||||
set({
|
|
||||||
current: first ?? null,
|
|
||||||
queue: rest,
|
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
act: (action, _reason) => {
|
setItems: (items) => {
|
||||||
const { current, queue } = get();
|
const [first, ...rest] = items;
|
||||||
const [next, ...rest] = queue;
|
set({
|
||||||
|
current: first ?? null,
|
||||||
|
queue: rest,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
// Log the decision (will be wired to backend decision log in Phase 7)
|
act: (action, _reason) => {
|
||||||
console.debug("[focus] act:", action, "on:", current?.id);
|
const { current, queue } = get();
|
||||||
|
const [next, ...rest] = queue;
|
||||||
|
|
||||||
const nextItem = next ?? null;
|
// Log the decision (will be wired to backend decision log in Phase 7)
|
||||||
set({
|
console.debug("[focus] act:", action, "on:", current?.id);
|
||||||
current: nextItem,
|
|
||||||
queue: rest,
|
|
||||||
});
|
|
||||||
|
|
||||||
return nextItem;
|
const nextItem = next ?? null;
|
||||||
},
|
set({
|
||||||
|
current: nextItem,
|
||||||
|
queue: rest,
|
||||||
|
});
|
||||||
|
|
||||||
setFocus: (itemId) => {
|
return nextItem;
|
||||||
const { current, queue } = get();
|
},
|
||||||
const allItems = current ? [current, ...queue] : [...queue];
|
|
||||||
const target = allItems.find((item) => item.id === itemId);
|
|
||||||
|
|
||||||
if (!target) return;
|
setFocus: (itemId) => {
|
||||||
|
const { current, queue } = get();
|
||||||
|
const allItems = current ? [current, ...queue] : [...queue];
|
||||||
|
const target = allItems.find((item) => item.id === itemId);
|
||||||
|
|
||||||
const remaining = allItems.filter((item) => item.id !== itemId);
|
if (!target) return;
|
||||||
set({
|
|
||||||
current: target,
|
|
||||||
queue: remaining,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
reorderQueue: (fromIndex, toIndex) => {
|
const remaining = allItems.filter((item) => item.id !== itemId);
|
||||||
const { queue } = get();
|
set({
|
||||||
if (
|
current: target,
|
||||||
fromIndex < 0 ||
|
queue: remaining,
|
||||||
fromIndex >= queue.length ||
|
});
|
||||||
toIndex < 0 ||
|
},
|
||||||
toIndex >= queue.length ||
|
|
||||||
fromIndex === toIndex
|
reorderQueue: (fromIndex, toIndex) => {
|
||||||
) {
|
const { queue } = get();
|
||||||
return;
|
if (
|
||||||
|
fromIndex < 0 ||
|
||||||
|
fromIndex >= queue.length ||
|
||||||
|
toIndex < 0 ||
|
||||||
|
toIndex >= queue.length ||
|
||||||
|
fromIndex === toIndex
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = [...queue];
|
||||||
|
const [moved] = updated.splice(fromIndex, 1);
|
||||||
|
updated.splice(toIndex, 0, moved);
|
||||||
|
|
||||||
|
console.debug("[focus] reorder:", moved.id, "from", fromIndex, "to", toIndex);
|
||||||
|
set({ queue: updated });
|
||||||
|
},
|
||||||
|
|
||||||
|
setLoading: (loading) => set({ isLoading: loading }),
|
||||||
|
setError: (error) => set({ error }),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: "mc-focus-store",
|
||||||
|
partialize: (state) => ({
|
||||||
|
current: state.current,
|
||||||
|
queue: state.queue,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
|
)
|
||||||
const updated = [...queue];
|
);
|
||||||
const [moved] = updated.splice(fromIndex, 1);
|
|
||||||
updated.splice(toIndex, 0, moved);
|
|
||||||
|
|
||||||
console.debug("[focus] reorder:", moved.id, "from", fromIndex, "to", toIndex);
|
|
||||||
set({ queue: updated });
|
|
||||||
},
|
|
||||||
|
|
||||||
setLoading: (loading) => set({ isLoading: loading }),
|
|
||||||
setError: (error) => set({ error }),
|
|
||||||
}));
|
|
||||||
|
|||||||
Reference in New Issue
Block a user