Skip to content

Commit 8ee9bb9

Browse files
committed
feat: local storage management.
1 parent f4ec7c1 commit 8ee9bb9

File tree

5 files changed

+185
-94
lines changed

5 files changed

+185
-94
lines changed

src/components/Sidebar.js

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,42 @@ import Fez from './Fez';
3434

3535
import { version } from '../version';
3636

37+
import usePersistentState from '../hooks/usePersistentState';
38+
import {
39+
KEY_SIDEBAR_IS_MAIN_OPEN,
40+
KEY_SIDEBAR_IS_CONTENT_OPEN,
41+
KEY_SIDEBAR_IS_APPS_OPEN,
42+
KEY_SIDEBAR_IS_EXTRAS_OPEN,
43+
KEY_SIDEBAR_IS_GAMES_OPEN,
44+
KEY_SIDEBAR_IS_EXTERNAL_LINKS_OPEN,
45+
} from '../utils/LocalStorageManager';
46+
3747
const Sidebar = ({ isOpen, toggleSidebar, toggleModal }) => {
38-
const [isMainOpen, setIsMainOpen] = useState(true);
39-
const [isContentOpen, setIsContentOpen] = useState(true);
40-
const [isAppsOpen, setIsAppsOpen] = useState(true);
41-
const [isExtrasOpen, setIsExtrasOpen] = useState(true);
42-
const [isGamesOpen, setIsGamesOpen] = useState(false);
43-
const [isExternalLinksOpen, setIsExternalLinksOpen] = useState(false);
48+
const [isMainOpen, setIsMainOpen] = usePersistentState(
49+
KEY_SIDEBAR_IS_MAIN_OPEN,
50+
true,
51+
);
52+
const [isContentOpen, setIsContentOpen] = usePersistentState(
53+
KEY_SIDEBAR_IS_CONTENT_OPEN,
54+
true,
55+
);
56+
const [isAppsOpen, setIsAppsOpen] = usePersistentState(
57+
KEY_SIDEBAR_IS_APPS_OPEN,
58+
true,
59+
);
60+
const [isExtrasOpen, setIsExtrasOpen] = usePersistentState(
61+
KEY_SIDEBAR_IS_EXTRAS_OPEN,
62+
false,
63+
);
64+
const [isGamesOpen, setIsGamesOpen] = usePersistentState(
65+
KEY_SIDEBAR_IS_GAMES_OPEN,
66+
false,
67+
);
68+
const [isExternalLinksOpen, setIsExternalLinksOpen] = usePersistentState(
69+
KEY_SIDEBAR_IS_EXTERNAL_LINKS_OPEN,
70+
false,
71+
);
72+
4473
const [allSectionsOpen, setAllSectionsOpen] = useState(true); // New state for collapse all
4574
const navigate = useNavigate(); // Initialize useNavigate
4675

src/context/AnimationContext.js

Lines changed: 14 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,24 @@
1-
import React, { createContext, useContext, useState, useEffect } from 'react';
1+
import React, { createContext, useContext } from 'react';
2+
import usePersistentState from '../hooks/usePersistentState';
3+
import {
4+
KEY_IS_ANIMATION_ENABLED,
5+
KEY_SHOW_ANIMATIONS_HOMEPAGE,
6+
KEY_SHOW_ANIMATIONS_INNER_PAGES,
7+
} from '../utils/LocalStorageManager';
28

39
const AnimationContext = createContext();
410

511
export const AnimationProvider = ({ children }) => {
6-
// Initialize state from localStorage, or default to true
7-
const [isAnimationEnabled, setIsAnimationEnabled] = useState(() => {
8-
try {
9-
const storedValue = localStorage.getItem('isAnimationEnabled');
10-
return storedValue ? JSON.parse(storedValue) : true;
11-
} catch (error) {
12-
console.error(
13-
"Error reading 'isAnimationEnabled' from localStorage",
14-
error,
15-
);
16-
return true; // Default to true if localStorage is not accessible
17-
}
18-
});
19-
20-
const [showAnimationsHomepage, setShowAnimationsHomepage] = useState(() => {
21-
try {
22-
const storedValue = localStorage.getItem('showAnimationsHomepage');
23-
return storedValue ? JSON.parse(storedValue) : true; // Default to true
24-
} catch (error) {
25-
console.error(
26-
"Error reading 'showAnimationsHomepage' from localStorage",
27-
error,
28-
);
29-
return true; // Default to true if localStorage is not accessible
30-
}
31-
});
32-
33-
const [showAnimationsInnerPages, setShowAnimationsInnerPages] = useState(
34-
() => {
35-
try {
36-
const storedValue = localStorage.getItem('showAnimationsInnerPages');
37-
return storedValue ? JSON.parse(storedValue) : false; // Default to false
38-
} catch (error) {
39-
console.error(
40-
"Error reading 'showAnimationsInnerPages' from localStorage",
41-
error,
42-
);
43-
return false; // Default to false if localStorage is not accessible
44-
}
45-
},
12+
const [isAnimationEnabled, setIsAnimationEnabled] = usePersistentState(
13+
KEY_IS_ANIMATION_ENABLED,
14+
true,
4615
);
4716

48-
// Update localStorage whenever isAnimationEnabled changes
49-
useEffect(() => {
50-
try {
51-
localStorage.setItem(
52-
'isAnimationEnabled',
53-
JSON.stringify(isAnimationEnabled),
54-
);
55-
} catch (error) {
56-
console.error(
57-
"Error writing 'isAnimationEnabled' to localStorage",
58-
error,
59-
);
60-
}
61-
}, [isAnimationEnabled]);
62-
63-
// Update localStorage whenever showAnimationsHomepage changes
64-
useEffect(() => {
65-
try {
66-
localStorage.setItem(
67-
'showAnimationsHomepage',
68-
JSON.stringify(showAnimationsHomepage),
69-
);
70-
} catch (error) {
71-
console.error(
72-
"Error writing 'showAnimationsHomepage' to localStorage",
73-
error,
74-
);
75-
}
76-
}, [showAnimationsHomepage]);
17+
const [showAnimationsHomepage, setShowAnimationsHomepage] =
18+
usePersistentState(KEY_SHOW_ANIMATIONS_HOMEPAGE, true);
7719

78-
// Update localStorage whenever showAnimationsInnerPages changes
79-
useEffect(() => {
80-
try {
81-
localStorage.setItem(
82-
'showAnimationsInnerPages',
83-
JSON.stringify(showAnimationsInnerPages),
84-
);
85-
} catch (error) {
86-
console.error(
87-
"Error writing 'showAnimationsInnerPages' to localStorage",
88-
error,
89-
);
90-
}
91-
}, [showAnimationsInnerPages]);
20+
const [showAnimationsInnerPages, setShowAnimationsInnerPages] =
21+
usePersistentState(KEY_SHOW_ANIMATIONS_INNER_PAGES, false);
9222

9323
const toggleAnimation = () => {
9424
setIsAnimationEnabled((prev) => !prev);

src/hooks/usePersistentState.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useState, useEffect } from 'react';
2+
import * as LocalStorageManager from '../utils/LocalStorageManager';
3+
4+
/**
5+
* A custom hook that uses useState and persists the state to localStorage.
6+
*
7+
* @param {string} key The key to use in localStorage.
8+
* @param {*} defaultValue The default value to use if nothing is in localStorage.
9+
* @returns A stateful value, and a function to update it.
10+
*/
11+
const usePersistentState = (key, defaultValue) => {
12+
const [state, setState] = useState(() =>
13+
LocalStorageManager.get(key, defaultValue),
14+
);
15+
16+
useEffect(() => {
17+
LocalStorageManager.set(key, state);
18+
}, [key, state]);
19+
20+
return [state, setState];
21+
};
22+
23+
export default usePersistentState;

src/pages/SettingsPage.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import { useAnimation } from '../context/AnimationContext';
55
import colors from '../config/colors';
66
import CustomToggle from '../components/CustomToggle';
77
import useSeo from '../hooks/useSeo';
8+
import { useToast } from '../hooks/useToast';
9+
import { SIDEBAR_KEYS } from '../utils/LocalStorageManager';
10+
import { remove as removeLocalStorageItem } from '../utils/LocalStorageManager';
811

912
const SettingsPage = () => {
1013
useSeo({
@@ -27,6 +30,23 @@ const SettingsPage = () => {
2730
showAnimationsInnerPages,
2831
toggleShowAnimationsInnerPages,
2932
} = useAnimation();
33+
const { addToast } = useToast();
34+
35+
const handleResetSidebarState = () => {
36+
SIDEBAR_KEYS.forEach((key) => {
37+
removeLocalStorageItem(key);
38+
});
39+
40+
addToast({
41+
title: 'Success',
42+
message: 'Sidebar state has been reset. The page will now reload.',
43+
duration: 3000,
44+
});
45+
46+
setTimeout(() => {
47+
window.location.reload();
48+
}, 3000);
49+
};
3050

3151
const cardStyle = {
3252
backgroundColor: colors['app-alpha-10'],
@@ -67,8 +87,7 @@ const SettingsPage = () => {
6787
></div>
6888
<div className="relative z-10 p-1">
6989
<h1 className="text-3xl font-arvo font-normal mb-4 text-app">
70-
{' '}
71-
Application Settings{' '}
90+
Application Settings
7291
</h1>
7392
<hr className="border-gray-700 mb-4" />
7493

@@ -85,8 +104,7 @@ const SettingsPage = () => {
85104
</div>
86105

87106
<h1 className="text-3xl font-arvo font-normal mb-4 text-app">
88-
{' '}
89-
Animation Settings{' '}
107+
Animation Settings
90108
</h1>
91109
<hr className="border-gray-700 mb-4" />
92110

@@ -129,6 +147,24 @@ const SettingsPage = () => {
129147
</div>
130148
)}
131149
</div>
150+
151+
{/* Sidebar Stuff */}
152+
<h1 className="text-3xl font-arvo font-normal mb-4 text-app">
153+
Sidebar Settings
154+
</h1>
155+
<hr className="border-gray-700 mb-4" />
156+
<div className="mb-6 ml-4 mr-4">
157+
<p className="text-gray-300 mb-4">
158+
Reset the open/closed state of all sidebar sections to their
159+
default.
160+
</p>
161+
<button
162+
onClick={handleResetSidebarState}
163+
className="px-6 py-2 rounded-md font-arvo font-normal transition-colors duration-300 ease-in-out border bg-red-800/50 text-white hover:bg-red-700/50 border-red-700"
164+
>
165+
Reset Sidebar State
166+
</button>
167+
</div>
132168
</div>
133169
</div>
134170
</div>

src/utils/LocalStorageManager.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* LocalStorageManager.js
3+
*
4+
* A centralized module for managing localStorage keys and interactions.
5+
* This prevents key name collisions and standardizes getting/setting values.
6+
*/
7+
8+
// --- Key Definitions ---
9+
10+
// Animation Settings
11+
export const KEY_IS_ANIMATION_ENABLED = 'isAnimationEnabled';
12+
export const KEY_SHOW_ANIMATIONS_HOMEPAGE = 'showAnimationsHomepage';
13+
export const KEY_SHOW_ANIMATIONS_INNER_PAGES = 'showAnimationsInnerPages';
14+
15+
// Sidebar Section States
16+
export const KEY_SIDEBAR_IS_MAIN_OPEN = 'sidebar_isMainOpen';
17+
export const KEY_SIDEBAR_IS_CONTENT_OPEN = 'sidebar_isContentOpen';
18+
export const KEY_SIDEBAR_IS_APPS_OPEN = 'sidebar_isAppsOpen';
19+
export const KEY_SIDEBAR_IS_EXTRAS_OPEN = 'sidebar_isExtrasOpen';
20+
export const KEY_SIDEBAR_IS_GAMES_OPEN = 'sidebar_isGamesOpen';
21+
export const KEY_SIDEBAR_IS_EXTERNAL_LINKS_OPEN = 'sidebar_isExternalLinksOpen';
22+
23+
export const SIDEBAR_KEYS = [
24+
KEY_SIDEBAR_IS_MAIN_OPEN,
25+
KEY_SIDEBAR_IS_CONTENT_OPEN,
26+
KEY_SIDEBAR_IS_APPS_OPEN,
27+
KEY_SIDEBAR_IS_EXTRAS_OPEN,
28+
KEY_SIDEBAR_IS_GAMES_OPEN,
29+
KEY_SIDEBAR_IS_EXTERNAL_LINKS_OPEN,
30+
];
31+
32+
// --- Utility Functions ---
33+
34+
/**
35+
* Safely gets and parses a value from localStorage.
36+
* @param {string} key The localStorage key.
37+
* @param {*} defaultValue The default value to return if the key doesn't exist or an error occurs.
38+
* @returns {*} The parsed value or the default value.
39+
*/
40+
export const get = (key, defaultValue) => {
41+
try {
42+
const storedValue = localStorage.getItem(key);
43+
return storedValue ? JSON.parse(storedValue) : defaultValue;
44+
} catch (error) {
45+
console.error(`Error reading '${key}' from localStorage`, error);
46+
return defaultValue;
47+
}
48+
};
49+
50+
/**
51+
* Safely sets a value in localStorage.
52+
* @param {string} key The localStorage key.
53+
* @param {*} value The value to be stringified and stored.
54+
*/
55+
export const set = (key, value) => {
56+
try {
57+
localStorage.setItem(key, JSON.stringify(value));
58+
} catch (error) {
59+
console.error(`Error writing '${key}' to localStorage`, error);
60+
}
61+
};
62+
63+
/**
64+
* Safely removes a value from localStorage.
65+
* @param {string} key The localStorage key to remove.
66+
*/
67+
export const remove = (key) => {
68+
try {
69+
localStorage.removeItem(key);
70+
} catch (error) {
71+
console.error(`Error removing '${key}' from localStorage`, error);
72+
}
73+
};

0 commit comments

Comments
 (0)