refactor: organize desktop vs. mobile into separate folders

This commit is contained in:
Arjun Patel
2026-04-29 08:42:56 -07:00
parent 3d9fe67936
commit 3a11a82cd3
194 changed files with 213 additions and 213 deletions
+131
View File
@@ -0,0 +1,131 @@
import { create } from "zustand";
import { signInWithCustomToken, signOut as firebaseSignOut } from "firebase/auth";
import { apiClient } from "@/api/client";
import type { Human } from "@/api/types";
import { firebaseAuth } from "@/firebase";
import { logError, ApiError } from "@/lib/errors";
import { useSessionStore } from "./session-store";
async function signInToFirebase() {
try {
const { token } = await apiClient.getFirebaseToken();
await signInWithCustomToken(firebaseAuth, token);
} catch (err) {
// Firestore subscriptions will fail until the next successful sign-in; the
// rest of the app keeps working against Orion.
logError(err, { scope: "auth.firebase" });
}
}
type AuthStatus = "idle" | "restoring" | "unauthenticated" | "authenticated";
interface AuthState {
status: AuthStatus;
user: Human | null;
isRequestingCode: boolean;
isSigningIn: boolean;
isSigningOut: boolean;
error: string | null;
restoreSession: () => Promise<void>;
requestCode: (email: string) => Promise<void>;
signIn: (email: string, code: string) => Promise<void>;
signOut: () => Promise<void>;
clearError: () => void;
}
export const useAuthStore = create<AuthState>((set) => ({
status: "idle",
user: null,
isRequestingCode: false,
isSigningIn: false,
isSigningOut: false,
error: null,
restoreSession: async () => {
const token = useSessionStore.getState().token;
if (!token) {
set({ status: "unauthenticated" });
return;
}
set({ status: "restoring" });
try {
const user = await apiClient.me();
await signInToFirebase();
set({ status: "authenticated", user });
} catch (err) {
// Expected on expired/invalid tokens — fall back to the login screen.
logError(err, { scope: "auth.restore" });
useSessionStore.getState().clearToken();
set({ status: "unauthenticated", user: null });
}
},
requestCode: async (email: string) => {
set({ isRequestingCode: true, error: null });
try {
await apiClient.requestCode({ email });
} catch (e) {
const message =
e instanceof ApiError ? e.message : "Failed to send code";
set({ error: message });
throw e;
} finally {
set({ isRequestingCode: false });
}
},
signIn: async (email: string, code: string) => {
set({ isSigningIn: true, error: null });
try {
const { human, token } = await apiClient.signIn({ email, code });
useSessionStore.getState().setToken(token);
await signInToFirebase();
set({ status: "authenticated", user: human });
} catch (e) {
const message =
e instanceof ApiError ? e.message : "Failed to sign in";
set({ error: message });
throw e;
} finally {
set({ isSigningIn: false });
}
},
signOut: async () => {
set({ isSigningOut: true });
try {
await apiClient.signOut();
} catch (err) {
// Best-effort — sign out locally regardless.
logError(err, { scope: "auth.signOut" });
} finally {
await firebaseSignOut(firebaseAuth).catch((err) =>
logError(err, { scope: "auth.firebaseSignOut" }),
);
useSessionStore.getState().clearToken();
set({
status: "unauthenticated",
user: null,
isSigningOut: false,
error: null,
});
}
},
clearError: () => set({ error: null }),
}));
// React to token being cleared externally (e.g. 401 from API client)
useSessionStore.subscribe((state, prevState) => {
if (prevState.token && !state.token) {
const authState = useAuthStore.getState();
if (authState.status === "authenticated") {
useAuthStore.setState({
status: "unauthenticated",
user: null,
error: null,
});
}
}
});
+35
View File
@@ -0,0 +1,35 @@
import { create } from "zustand";
const STORAGE_KEY = "llink:autoplay-muted";
function loadMuted(): boolean {
try {
return localStorage.getItem(STORAGE_KEY) === "true";
} catch {
return false;
}
}
function saveMuted(muted: boolean) {
try {
localStorage.setItem(STORAGE_KEY, String(muted));
} catch {
// Storage unavailable
}
}
interface AutoplayState {
/** When true, incoming play requests are ignored */
muted: boolean;
/** Toggle muted state */
toggleMuted: () => void;
}
export const useAutoplayStore = create<AutoplayState>((set, get) => ({
muted: loadMuted(),
toggleMuted: () => {
const next = !get().muted;
set({ muted: next });
saveMuted(next);
},
}));
@@ -0,0 +1,50 @@
import { create } from "zustand";
export interface SavedDevice {
deviceId: string;
label: string;
}
const MIC_KEY = "llink:mic-device";
const CAMERA_KEY = "llink:camera-device";
function load(key: string): SavedDevice | null {
try {
const raw = localStorage.getItem(key);
if (!raw) return null;
const parsed = JSON.parse(raw) as SavedDevice;
if (typeof parsed?.deviceId !== "string") return null;
return { deviceId: parsed.deviceId, label: parsed.label ?? "" };
} catch {
return null;
}
}
function save(key: string, value: SavedDevice | null) {
try {
if (value) localStorage.setItem(key, JSON.stringify(value));
else localStorage.removeItem(key);
} catch {
// Storage unavailable
}
}
interface MediaDevicesState {
mic: SavedDevice | null;
camera: SavedDevice | null;
setMic: (device: SavedDevice | null) => void;
setCamera: (device: SavedDevice | null) => void;
}
export const useMediaDevicesStore = create<MediaDevicesState>((set) => ({
mic: load(MIC_KEY),
camera: load(CAMERA_KEY),
setMic: (device) => {
save(MIC_KEY, device);
set({ mic: device });
},
setCamera: (device) => {
save(CAMERA_KEY, device);
set({ camera: device });
},
}));
@@ -0,0 +1,21 @@
import { create } from "zustand";
export type RecordingMode = "video" | "audio";
const KEY = "llink:recording-mode";
interface MediaSettingsState {
recordingMode: RecordingMode;
setRecordingMode: (mode: RecordingMode) => void;
}
export const useMediaSettingsStore = create<MediaSettingsState>((set) => ({
recordingMode: (() => {
const stored = localStorage.getItem(KEY);
return stored === "audio" ? "audio" : "video";
})(),
setRecordingMode: (mode) => {
localStorage.setItem(KEY, mode);
set({ recordingMode: mode });
},
}));
@@ -0,0 +1,27 @@
import { create } from "zustand";
/**
* Single source of truth for "is stream playback paused." Each component that
* wants to pause playback registers a unique id via `useSuspendPlayback`; the
* label is for devtools only. Playback is paused while any id is registered.
*/
interface PlaybackPauseState {
activeIds: Record<string, string>;
add: (id: string, label: string) => void;
remove: (id: string) => void;
}
export const usePlaybackPauseStore = create<PlaybackPauseState>((set) => ({
activeIds: {},
add: (id, label) =>
set((s) => ({ activeIds: { ...s.activeIds, [id]: label } })),
remove: (id) =>
set((s) => {
if (!(id in s.activeIds)) return s;
const { [id]: _, ...rest } = s.activeIds;
return { activeIds: rest };
}),
}));
export const selectIsPaused = (s: PlaybackPauseState) =>
Object.keys(s.activeIds).length > 0;
+23
View File
@@ -0,0 +1,23 @@
import { create } from "zustand";
const AUTH_TOKEN_KEY = "auth_token";
interface SessionState {
token: string | null;
setToken: (token: string) => void;
clearToken: () => void;
}
export const useSessionStore = create<SessionState>((set) => ({
token: localStorage.getItem(AUTH_TOKEN_KEY),
setToken: (token: string) => {
localStorage.setItem(AUTH_TOKEN_KEY, token);
set({ token });
},
clearToken: () => {
localStorage.removeItem(AUTH_TOKEN_KEY);
set({ token: null });
},
}));
@@ -0,0 +1,40 @@
import { create } from "zustand";
import { soundEffects } from "@/lib/sound-effects/engine";
const ENABLED_KEY = "llink:soundEffects-enabled";
const DEFAULT_ENABLED = true;
function loadEnabled(): boolean {
try {
const v = localStorage.getItem(ENABLED_KEY);
return v === null ? DEFAULT_ENABLED : v === "true";
} catch {
return DEFAULT_ENABLED;
}
}
interface SoundEffectsState {
enabled: boolean;
setEnabled: (enabled: boolean) => void;
}
export const useSoundEffectsStore = create<SoundEffectsState>((set) => {
const initialEnabled = loadEnabled();
// Sync engine with persisted state on first load. Master volume is a fixed
// tuning constant in the engine — no UI control.
soundEffects.setOptions({ enabled: initialEnabled });
return {
enabled: initialEnabled,
setEnabled: (enabled) => {
set({ enabled });
soundEffects.setOptions({ enabled });
try {
localStorage.setItem(ENABLED_KEY, String(enabled));
} catch {
// storage unavailable
}
},
};
});