Live video on the AT Protocol
1import { createAppSlice } from "../../hooks/createSlice";
2import WebStorage from "../../storage/storage";
3
4const storage = new WebStorage();
5export const SIDEBAR_STORAGE_KEY = "sidebarState";
6
7export interface SidebarState {
8 isCollapsed: boolean;
9 // should only be used in fullscreen
10 isHidden: boolean;
11 targetWidth: number;
12 isLoaded: boolean;
13}
14
15const initialState: SidebarState = {
16 isCollapsed: false,
17 isHidden: false,
18 targetWidth: 250,
19 isLoaded: false,
20};
21
22function verifySidebarState(state: any): SidebarState {
23 const verifiedState: SidebarState = {
24 isCollapsed:
25 typeof state.isCollapsed === "boolean" ? state.isCollapsed : false,
26 isHidden: typeof state.isHidden === "boolean" ? state.isHidden : false,
27 targetWidth:
28 typeof state.targetWidth === "number" ? state.targetWidth : 250,
29 isLoaded: false,
30 };
31
32 if (!verifiedState.isHidden) {
33 if (verifiedState.targetWidth < 64) {
34 verifiedState.targetWidth = 64;
35 }
36 } else {
37 verifiedState.targetWidth = 0;
38 }
39
40 return verifiedState;
41}
42
43export const sidebarSlice = createAppSlice({
44 name: "sidebar",
45 initialState,
46 reducers: (create) => ({
47 setSidebarHidden: create.reducer((state) => {
48 state.isHidden = true;
49 state.targetWidth =
50 state.isCollapsed || state.isHidden ? (state.isHidden ? 0 : 64) : 250;
51 }),
52 setSidebarUnhidden: create.reducer((state) => {
53 state.isHidden = false;
54 state.targetWidth =
55 state.isCollapsed || state.isHidden ? (state.isHidden ? 0 : 64) : 250;
56 }),
57 toggleSidebar: create.reducer((state) => {
58 state.isCollapsed = !state.isCollapsed;
59 state.targetWidth =
60 state.isCollapsed || state.isHidden ? (state.isHidden ? 0 : 64) : 250;
61 }),
62 loadStateFromStorage: create.asyncThunk(
63 async () => {
64 const storedStateString = await storage.getItem(SIDEBAR_STORAGE_KEY);
65 if (storedStateString) {
66 let state = JSON.parse(storedStateString);
67 // should never be 'true' on load, component should ALWAYS request to hide sidebar when loaded
68 state.isHidden = false;
69 return verifySidebarState(state) as SidebarState;
70 }
71 return null;
72 },
73 {
74 pending: (state) => {
75 // unlikely that this will hang for a noticeable duration
76 },
77 fulfilled: (state, action) => {
78 if (action.payload) {
79 state.isCollapsed = action.payload.isCollapsed;
80 state.targetWidth = action.payload.targetWidth;
81 console.log(
82 "Sidebar state loaded from localStorage:",
83 action.payload,
84 );
85 } else {
86 console.log(
87 "No sidebar state found in localStorage, using defaults.",
88 );
89 }
90 state.isLoaded = true;
91 },
92 rejected: (state, action) => {
93 state.isLoaded = true;
94 console.error(
95 "Failed to load sidebar state from storage, using defaults:",
96 action.error,
97 );
98 // use defaults
99 state.isCollapsed = false;
100 state.targetWidth = 250;
101 },
102 },
103 ),
104 }),
105 selectors: {
106 selectIsSidebarCollapsed: (state) => state.isCollapsed,
107 selectSidebarTargetWidth: (state) => state.targetWidth,
108 selectIsSidebarLoaded: (state) => state.isLoaded,
109 selectIsSidebarHidden: (state) => state.isHidden,
110 },
111});
112
113export const {
114 toggleSidebar,
115 loadStateFromStorage,
116 setSidebarHidden,
117 setSidebarUnhidden,
118} = sidebarSlice.actions;
119export const {
120 selectIsSidebarCollapsed,
121 selectSidebarTargetWidth,
122 selectIsSidebarLoaded,
123 selectIsSidebarHidden,
124} = sidebarSlice.selectors;