137 lines
2.9 KiB
TypeScript
137 lines
2.9 KiB
TypeScript
export type AppPage = 'auth' | 'onboarding' | 'camera' | 'client' | 'activity' | 'settings';
|
|
|
|
export type ToastType = 'info' | 'success' | 'error';
|
|
|
|
export type AppToast = {
|
|
id: string;
|
|
type: ToastType;
|
|
message: string;
|
|
};
|
|
|
|
export type LinkedCamera = {
|
|
id: string;
|
|
cameraDeviceId: string;
|
|
clientDeviceId: string;
|
|
cameraName?: string | null;
|
|
cameraStatus?: string | null;
|
|
};
|
|
|
|
export type MotionNotification = {
|
|
id: string;
|
|
cameraDeviceId: string;
|
|
message: string;
|
|
createdAt: string;
|
|
isRead: boolean;
|
|
};
|
|
|
|
export type ActivityLogItem = {
|
|
id: string;
|
|
type: string;
|
|
message: string;
|
|
createdAt: string;
|
|
};
|
|
|
|
export type RecordingItem = {
|
|
id: string;
|
|
status?: string;
|
|
createdAt: string;
|
|
cameraDeviceId?: string;
|
|
durationSeconds?: number | null;
|
|
streamSessionId?: string;
|
|
};
|
|
|
|
export type Device = {
|
|
id: string;
|
|
name: string;
|
|
role: 'camera' | 'client';
|
|
status?: string;
|
|
};
|
|
|
|
export type Session = {
|
|
user?: {
|
|
id?: string;
|
|
name?: string;
|
|
email?: string;
|
|
};
|
|
session?: {
|
|
id?: string;
|
|
};
|
|
};
|
|
|
|
export type AppState = {
|
|
page: AppPage;
|
|
session: Session | null;
|
|
device: Device | null;
|
|
deviceToken: string | null;
|
|
socketConnected: boolean;
|
|
isMotionActive: boolean;
|
|
cameraPermissionGranted: boolean;
|
|
cameraPreviewReady: boolean;
|
|
cameraStatus: 'idle' | 'recording';
|
|
linkedCameras: LinkedCamera[];
|
|
recordings: RecordingItem[];
|
|
motionNotifications: MotionNotification[];
|
|
activeCameraDeviceId: string | null;
|
|
activeStreamSessionId: string | null;
|
|
activityLog: ActivityLogItem[];
|
|
cameraSessions: Record<string, string>;
|
|
connectedStreamSessionIds: string[];
|
|
loading: boolean;
|
|
isRegistering: boolean;
|
|
authForm: {
|
|
email: string;
|
|
password: string;
|
|
name: string;
|
|
};
|
|
onboardingForm: {
|
|
name: string;
|
|
role: 'camera' | 'client';
|
|
pushToken: string;
|
|
};
|
|
toasts: AppToast[];
|
|
clientStreamMode: 'none' | 'connecting' | 'unavailable' | 'image' | 'video';
|
|
clientFallbackFrame: string;
|
|
clientPlaceholderText: string;
|
|
lastError: string | null;
|
|
};
|
|
|
|
export const createInitialState = (): AppState => ({
|
|
page: 'auth',
|
|
session: null,
|
|
device: null,
|
|
deviceToken: null,
|
|
socketConnected: false,
|
|
isMotionActive: false,
|
|
cameraPermissionGranted: false,
|
|
cameraPreviewReady: false,
|
|
cameraStatus: 'idle',
|
|
linkedCameras: [],
|
|
recordings: [],
|
|
motionNotifications: [],
|
|
activeCameraDeviceId: null,
|
|
activeStreamSessionId: null,
|
|
activityLog: [],
|
|
cameraSessions: {},
|
|
connectedStreamSessionIds: [],
|
|
loading: false,
|
|
isRegistering: false,
|
|
authForm: {
|
|
email: '',
|
|
password: '',
|
|
name: '',
|
|
},
|
|
onboardingForm: {
|
|
name: '',
|
|
role: 'client',
|
|
pushToken: '',
|
|
},
|
|
toasts: [],
|
|
clientStreamMode: 'none',
|
|
clientFallbackFrame: '',
|
|
clientPlaceholderText: 'Select a camera to view',
|
|
lastError: null,
|
|
});
|
|
|
|
export const unreadNotificationsCount = (state: AppState): number =>
|
|
state.motionNotifications.reduce((count, item) => count + (item.isRead ? 0 : 1), 0);
|