Add useStorageState hook (#37825)
This commit is contained in:
parent
bbd88d356d
commit
f99c60a8f3
@ -7,7 +7,7 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { useStorage } from '@/mastodon/hooks/useStorage';
|
import { useStorageState } from '@/mastodon/hooks/useStorage';
|
||||||
|
|
||||||
interface AccountTimelineContextValue {
|
interface AccountTimelineContextValue {
|
||||||
accountId: string;
|
accountId: string;
|
||||||
@ -26,30 +26,34 @@ export const AccountTimelineProvider: FC<{
|
|||||||
accountId: string;
|
accountId: string;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}> = ({ accountId, children }) => {
|
}> = ({ accountId, children }) => {
|
||||||
const { getItem, setItem } = useStorage({
|
const storageOptions = {
|
||||||
type: 'session',
|
type: 'session',
|
||||||
prefix: `filters-${accountId}:`,
|
prefix: `filters-${accountId}:`,
|
||||||
});
|
} as const;
|
||||||
const [boosts, setBoosts] = useState(
|
|
||||||
() => (getItem('boosts') === '0' ? false : true), // Default to enabled.
|
const [boosts, setBoosts] = useStorageState<boolean>(
|
||||||
|
'boosts',
|
||||||
|
true,
|
||||||
|
storageOptions,
|
||||||
);
|
);
|
||||||
const [replies, setReplies] = useState(() =>
|
|
||||||
getItem('replies') === '1' ? true : false,
|
const [replies, setReplies] = useStorageState<boolean>(
|
||||||
|
'replies',
|
||||||
|
false,
|
||||||
|
storageOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSetBoosts = useCallback(
|
const handleSetBoosts = useCallback(
|
||||||
(value: boolean) => {
|
(value: boolean) => {
|
||||||
setBoosts(value);
|
setBoosts(value);
|
||||||
setItem('boosts', value ? '1' : '0');
|
|
||||||
},
|
},
|
||||||
[setBoosts, setItem],
|
[setBoosts],
|
||||||
);
|
);
|
||||||
const handleSetReplies = useCallback(
|
const handleSetReplies = useCallback(
|
||||||
(value: boolean) => {
|
(value: boolean) => {
|
||||||
setReplies(value);
|
setReplies(value);
|
||||||
setItem('replies', value ? '1' : '0');
|
|
||||||
},
|
},
|
||||||
[setReplies, setItem],
|
[setReplies],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [showAllPinned, setShowAllPinned] = useState(false);
|
const [showAllPinned, setShowAllPinned] = useState(false);
|
||||||
|
|||||||
@ -1,9 +1,14 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
interface StorageOptions {
|
||||||
|
type?: 'local' | 'session';
|
||||||
|
prefix?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export function useStorage({
|
export function useStorage({
|
||||||
type = 'local',
|
type = 'local',
|
||||||
prefix = '',
|
prefix = '',
|
||||||
}: { type?: 'local' | 'session'; prefix?: string } = {}) {
|
}: StorageOptions = {}) {
|
||||||
const storageType = type === 'local' ? 'localStorage' : 'sessionStorage';
|
const storageType = type === 'local' ? 'localStorage' : 'sessionStorage';
|
||||||
const isAvailable = useMemo(
|
const isAvailable = useMemo(
|
||||||
() => storageAvailable(storageType),
|
() => storageAvailable(storageType),
|
||||||
@ -23,6 +28,7 @@ export function useStorage({
|
|||||||
},
|
},
|
||||||
[isAvailable, storageType, prefix],
|
[isAvailable, storageType, prefix],
|
||||||
);
|
);
|
||||||
|
|
||||||
const setItem = useCallback(
|
const setItem = useCallback(
|
||||||
(key: string, value: string) => {
|
(key: string, value: string) => {
|
||||||
if (!isAvailable) {
|
if (!isAvailable) {
|
||||||
@ -35,13 +41,52 @@ export function useStorage({
|
|||||||
[isAvailable, storageType, prefix],
|
[isAvailable, storageType, prefix],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const removeItem = useCallback(
|
||||||
|
(key: string) => {
|
||||||
|
if (!isAvailable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
window[storageType].removeItem(prefix ? `${prefix};${key}` : key);
|
||||||
|
} catch {}
|
||||||
|
},
|
||||||
|
[isAvailable, storageType, prefix],
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isAvailable,
|
isAvailable,
|
||||||
getItem,
|
getItem,
|
||||||
setItem,
|
setItem,
|
||||||
|
removeItem,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useStorageState<T extends string | boolean>(
|
||||||
|
key: string,
|
||||||
|
initialState: T,
|
||||||
|
options?: StorageOptions,
|
||||||
|
) {
|
||||||
|
const { getItem, setItem, removeItem } = useStorage(options);
|
||||||
|
const [state, setState] = useState<T>(
|
||||||
|
() => (retrieveBooleanOrString(getItem(key)) as T | null) ?? initialState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleSetState = useCallback(
|
||||||
|
(newValue: T) => {
|
||||||
|
setItem(key, castToString(newValue));
|
||||||
|
setState(newValue);
|
||||||
|
},
|
||||||
|
[key, setItem],
|
||||||
|
);
|
||||||
|
|
||||||
|
const removeState = useCallback(() => {
|
||||||
|
removeItem(key);
|
||||||
|
setState(initialState);
|
||||||
|
}, [initialState, key, removeItem]);
|
||||||
|
|
||||||
|
return [state, handleSetState, removeState] as const;
|
||||||
|
}
|
||||||
|
|
||||||
// Tests the storage availability for the given type. Taken from MDN:
|
// Tests the storage availability for the given type. Taken from MDN:
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
|
// https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
|
||||||
export function storageAvailable(type: 'localStorage' | 'sessionStorage') {
|
export function storageAvailable(type: 'localStorage' | 'sessionStorage') {
|
||||||
@ -62,3 +107,21 @@ export function storageAvailable(type: 'localStorage' | 'sessionStorage') {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function castToString(value: string | boolean) {
|
||||||
|
if (typeof value === 'boolean') {
|
||||||
|
return value ? '1' : '0';
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function retrieveBooleanOrString(value: string | null) {
|
||||||
|
if (value === '1') {
|
||||||
|
return true;
|
||||||
|
} else if (value === '0') {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user