mirror of https://github.com/jetkvm/kvm.git
175 lines
4.2 KiB
TypeScript
175 lines
4.2 KiB
TypeScript
import { create } from "zustand";
|
|
import { persist, createJSONStorage } from "zustand/middleware";
|
|
|
|
export type SessionMode = "primary" | "observer" | "queued" | "pending";
|
|
|
|
export interface SessionInfo {
|
|
id: string;
|
|
mode: SessionMode;
|
|
source: "local" | "cloud";
|
|
identity?: string;
|
|
nickname?: string;
|
|
createdAt: string;
|
|
lastActive: string;
|
|
}
|
|
|
|
export interface SessionState {
|
|
// Current session info
|
|
currentSessionId: string | null;
|
|
currentMode: SessionMode | null;
|
|
|
|
// All active sessions
|
|
sessions: SessionInfo[];
|
|
|
|
// UI state
|
|
isRequestingPrimary: boolean;
|
|
sessionError: string | null;
|
|
rejectionCount: number;
|
|
|
|
// Actions
|
|
setCurrentSession: (id: string, mode: SessionMode) => void;
|
|
setSessions: (sessions: SessionInfo[]) => void;
|
|
setRequestingPrimary: (requesting: boolean) => void;
|
|
setSessionError: (error: string | null) => void;
|
|
updateSessionMode: (mode: SessionMode) => void;
|
|
clearSession: () => void;
|
|
incrementRejectionCount: () => number;
|
|
resetRejectionCount: () => void;
|
|
|
|
// Computed getters
|
|
isPrimary: () => boolean;
|
|
isObserver: () => boolean;
|
|
isQueued: () => boolean;
|
|
isPending: () => boolean;
|
|
canRequestPrimary: () => boolean;
|
|
getPrimarySession: () => SessionInfo | undefined;
|
|
getQueuePosition: () => number;
|
|
}
|
|
|
|
export const useSessionStore = create<SessionState>()(
|
|
persist(
|
|
(set, get) => ({
|
|
// Initial state
|
|
currentSessionId: null,
|
|
currentMode: null,
|
|
sessions: [],
|
|
isRequestingPrimary: false,
|
|
sessionError: null,
|
|
rejectionCount: 0,
|
|
|
|
// Actions
|
|
setCurrentSession: (id: string, mode: SessionMode) => {
|
|
set({
|
|
currentSessionId: id,
|
|
currentMode: mode,
|
|
sessionError: null
|
|
});
|
|
},
|
|
|
|
setSessions: (sessions: SessionInfo[]) => {
|
|
set({ sessions });
|
|
},
|
|
|
|
setRequestingPrimary: (requesting: boolean) => {
|
|
set({ isRequestingPrimary: requesting });
|
|
},
|
|
|
|
setSessionError: (error: string | null) => {
|
|
set({ sessionError: error });
|
|
},
|
|
|
|
updateSessionMode: (mode: SessionMode) => {
|
|
set({ currentMode: mode });
|
|
},
|
|
|
|
clearSession: () => {
|
|
set({
|
|
currentSessionId: null,
|
|
currentMode: null,
|
|
sessions: [],
|
|
sessionError: null,
|
|
isRequestingPrimary: false,
|
|
rejectionCount: 0
|
|
});
|
|
},
|
|
|
|
incrementRejectionCount: () => {
|
|
const newCount = get().rejectionCount + 1;
|
|
set({ rejectionCount: newCount });
|
|
return newCount;
|
|
},
|
|
|
|
resetRejectionCount: () => {
|
|
set({ rejectionCount: 0 });
|
|
},
|
|
|
|
// Computed getters
|
|
isPrimary: () => {
|
|
return get().currentMode === "primary";
|
|
},
|
|
|
|
isObserver: () => {
|
|
return get().currentMode === "observer";
|
|
},
|
|
|
|
isQueued: () => {
|
|
return get().currentMode === "queued";
|
|
},
|
|
|
|
isPending: () => {
|
|
return get().currentMode === "pending";
|
|
},
|
|
|
|
canRequestPrimary: () => {
|
|
const state = get();
|
|
return state.currentMode === "observer" &&
|
|
!state.isRequestingPrimary &&
|
|
state.sessions.some(s => s.mode === "primary");
|
|
},
|
|
|
|
getPrimarySession: () => {
|
|
return get().sessions.find(s => s.mode === "primary");
|
|
},
|
|
|
|
getQueuePosition: () => {
|
|
const state = get();
|
|
if (state.currentMode !== "queued") return -1;
|
|
|
|
const queuedSessions = state.sessions
|
|
.filter(s => s.mode === "queued")
|
|
.sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime());
|
|
|
|
return queuedSessions.findIndex(s => s.id === state.currentSessionId) + 1;
|
|
}
|
|
}),
|
|
{
|
|
name: 'session',
|
|
storage: createJSONStorage(() => sessionStorage),
|
|
partialize: (state) => ({
|
|
currentSessionId: state.currentSessionId,
|
|
}),
|
|
}
|
|
)
|
|
);
|
|
|
|
// Shared session store - separate with localStorage (shared across tabs)
|
|
// Used for user preferences that should be consistent across all tabs
|
|
export interface SharedSessionState {
|
|
nickname: string | null;
|
|
setNickname: (nickname: string | null) => void;
|
|
clearNickname: () => void;
|
|
}
|
|
|
|
export const useSharedSessionStore = create<SharedSessionState>()(
|
|
persist(
|
|
(set) => ({
|
|
nickname: null,
|
|
setNickname: (nickname: string | null) => set({ nickname }),
|
|
clearNickname: () => set({ nickname: null }),
|
|
}),
|
|
{
|
|
name: 'sharedSession',
|
|
storage: createJSONStorage(() => localStorage),
|
|
}
|
|
)
|
|
); |