Files
llink/js/src/stores/compose-store.ts
T
2026-03-18 14:52:22 -07:00

107 lines
2.5 KiB
TypeScript

import { create } from "zustand";
export type ComposeStep =
| "idle"
| "recording"
| "reviewing"
| "typing"
| "configuring";
export type RecordingMode = "video" | "audio";
const RECORDING_MODE_KEY = "llink:recording-mode";
function loadRecordingMode(): RecordingMode {
const stored = localStorage.getItem(RECORDING_MODE_KEY);
return stored === "audio" ? "audio" : "video";
}
interface ComposeState {
// Flow
step: ComposeStep;
networkId: string | null;
error: string | null;
// Text compose
textContent: string;
// Recording
recordingMode: RecordingMode;
mediaStream: MediaStream | null;
reviewBlob: Blob | null;
reviewDurationMs: number;
// Transitions
startRecording: (networkId: string) => void;
finishRecording: (blob: Blob, durationMs: number) => void;
startTyping: (networkId: string) => void;
advanceToConfigure: () => void;
setTextContent: (text: string) => void;
cancel: () => void;
// Recording helpers (called by useRecorder)
setMediaStream: (stream: MediaStream | null) => void;
setRecordingMode: (mode: RecordingMode) => void;
setError: (error: string) => void;
}
export const useComposeStore = create<ComposeState>((set, get) => ({
step: "idle",
networkId: null,
error: null,
textContent: "",
recordingMode: loadRecordingMode(),
mediaStream: null,
reviewBlob: null,
reviewDurationMs: 0,
startRecording: (networkId) => {
if (get().step !== "idle") return;
set({
step: "recording",
networkId,
error: null,
reviewBlob: null,
reviewDurationMs: 0,
});
},
finishRecording: (blob, durationMs) => {
if (get().step !== "recording") return;
set({ step: "reviewing", reviewBlob: blob, reviewDurationMs: durationMs });
},
startTyping: (networkId) => {
if (get().step !== "idle") return;
set({ step: "typing", networkId, textContent: "", error: null });
},
advanceToConfigure: () => {
const { step } = get();
if (step !== "reviewing" && step !== "typing") return;
set({ step: "configuring" });
},
setTextContent: (textContent) => set({ textContent }),
cancel: () =>
set({
step: "idle",
networkId: null,
error: null,
textContent: "",
mediaStream: null,
reviewBlob: null,
reviewDurationMs: 0,
}),
setMediaStream: (mediaStream) => set({ mediaStream }),
setRecordingMode: (mode) => {
localStorage.setItem(RECORDING_MODE_KEY, mode);
set({ recordingMode: mode });
},
setError: (error) => set({ error }),
}));