feat: store notification filter state in localStorage
This commit is contained in:
parent
de6fe78004
commit
f9a5007104
@ -15,6 +15,8 @@ import dayjs from 'dayjs';
|
|||||||
import { Popup, usePopup } from 'shared/components/PopupMenu';
|
import { Popup, usePopup } from 'shared/components/PopupMenu';
|
||||||
import { CheckCircleOutline, Circle, CircleSolid, UserCircle } from 'shared/icons';
|
import { CheckCircleOutline, Circle, CircleSolid, UserCircle } from 'shared/icons';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
|
import { useLocalStorage } from 'shared/hooks/useStateWithLocalStorage';
|
||||||
|
import localStorage from 'shared/utils/localStorage';
|
||||||
|
|
||||||
const ItemWrapper = styled.div`
|
const ItemWrapper = styled.div`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -403,7 +405,10 @@ type NotificationEntry = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
const NotificationPopup: React.FC = ({ children }) => {
|
const NotificationPopup: React.FC = ({ children }) => {
|
||||||
const [filter, setFilter] = useState<NotificationFilter>(NotificationFilter.Unread);
|
const [filter, setFilter] = useLocalStorage<NotificationFilter>(
|
||||||
|
localStorage.NOTIFICATIONS_FILTER,
|
||||||
|
NotificationFilter.Unread,
|
||||||
|
);
|
||||||
const [data, setData] = useState<{ nodes: Array<NotificationEntry>; hasNextPage: boolean; cursor: string }>({
|
const [data, setData] = useState<{ nodes: Array<NotificationEntry>; hasNextPage: boolean; cursor: string }>({
|
||||||
nodes: [],
|
nodes: [],
|
||||||
hasNextPage: false,
|
hasNextPage: false,
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
import React from 'react';
|
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
// A wrapper for "JSON.parse()"" to support "undefined" value
|
||||||
|
function parseJSON<T>(value: string | null): T | undefined {
|
||||||
|
try {
|
||||||
|
return value === 'undefined' ? undefined : JSON.parse(value ?? '');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('parsing error on', { value });
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const useStateWithLocalStorage = (localStorageKey: string): [string, React.Dispatch<React.SetStateAction<string>>] => {
|
const useStateWithLocalStorage = (localStorageKey: string): [string, React.Dispatch<React.SetStateAction<string>>] => {
|
||||||
const [value, setValue] = React.useState<string>(localStorage.getItem(localStorageKey) || '');
|
const [value, setValue] = React.useState<string>(localStorage.getItem(localStorageKey) || '');
|
||||||
@ -11,3 +21,78 @@ const useStateWithLocalStorage = (localStorageKey: string): [string, React.Dispa
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default useStateWithLocalStorage;
|
export default useStateWithLocalStorage;
|
||||||
|
|
||||||
|
type SetValue<T> = Dispatch<SetStateAction<T>>;
|
||||||
|
|
||||||
|
function useLocalStorage<T>(key: string, initialValue: T): [T, SetValue<T>] {
|
||||||
|
// Get from local storage then
|
||||||
|
// parse stored json or return initialValue
|
||||||
|
const readValue = (): T => {
|
||||||
|
// Prevent build error "window is undefined" but keep keep working
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return initialValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const item = window.localStorage.getItem(key);
|
||||||
|
return item ? (parseJSON(item) as T) : initialValue;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Error reading localStorage key “${key}”:`, error);
|
||||||
|
return initialValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// State to store our value
|
||||||
|
// Pass initial state function to useState so logic is only executed once
|
||||||
|
const [storedValue, setStoredValue] = useState<T>(readValue);
|
||||||
|
|
||||||
|
// Return a wrapped version of useState's setter function that ...
|
||||||
|
// ... persists the new value to localStorage.
|
||||||
|
const setValue: SetValue<T> = (value) => {
|
||||||
|
// Prevent build error "window is undefined" but keeps working
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
console.warn(`Tried setting localStorage key “${key}” even though environment is not a client`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Allow value to be a function so we have the same API as useState
|
||||||
|
const newValue = value instanceof Function ? value(storedValue) : value;
|
||||||
|
|
||||||
|
// Save to local storage
|
||||||
|
window.localStorage.setItem(key, JSON.stringify(newValue));
|
||||||
|
|
||||||
|
// Save state
|
||||||
|
setStoredValue(newValue);
|
||||||
|
|
||||||
|
// We dispatch a custom event so every useLocalStorage hook are notified
|
||||||
|
window.dispatchEvent(new Event('local-storage'));
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Error setting localStorage key “${key}”:`, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setStoredValue(readValue());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handleStorageChange = () => {
|
||||||
|
setStoredValue(readValue());
|
||||||
|
};
|
||||||
|
|
||||||
|
// this only works for other documents, not the current one
|
||||||
|
window.addEventListener('storage', handleStorageChange);
|
||||||
|
|
||||||
|
// this is a custom event, triggered in writeValueToLocalStorage
|
||||||
|
window.addEventListener('local-storage', handleStorageChange);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('storage', handleStorageChange);
|
||||||
|
window.removeEventListener('local-storage', handleStorageChange);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return [storedValue, setValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useLocalStorage };
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const localStorage = {
|
const localStorage = {
|
||||||
|
NOTIFICATIONS_FILTER: 'notifications_filter',
|
||||||
CARD_LABEL_VARIANT_STORAGE_KEY: 'card_label_variant',
|
CARD_LABEL_VARIANT_STORAGE_KEY: 'card_label_variant',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user