mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-25 11:52:10 +00:00
Safer way to interact with localStorage (#2679)
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'gitbook': patch
|
||||
---
|
||||
|
||||
Implement a safer way to interact with localStorage.
|
||||
|
||||
If it's disabled on the browser it should not throw error.
|
||||
@@ -3,6 +3,7 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
|
||||
import { useHash, useIsMounted } from '@/components/hooks';
|
||||
import * as storage from '@/lib/local-storage';
|
||||
import { ClassValue, tcls } from '@/lib/tailwind';
|
||||
|
||||
interface TabsState {
|
||||
@@ -12,15 +13,12 @@ interface TabsState {
|
||||
activeTitles: string[];
|
||||
}
|
||||
|
||||
let globalTabsState: TabsState = (() => {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return { activeIds: {}, activeTitles: [] };
|
||||
}
|
||||
|
||||
const stored = localStorage.getItem('@gitbook/tabsState');
|
||||
return stored ? (JSON.parse(stored) as TabsState) : { activeIds: {}, activeTitles: [] };
|
||||
})();
|
||||
const defaultTabsState: TabsState = {
|
||||
activeIds: {},
|
||||
activeTitles: [],
|
||||
};
|
||||
|
||||
let globalTabsState = storage.getItem('@gitbook/tabsState', defaultTabsState);
|
||||
const listeners = new Set<() => void>();
|
||||
|
||||
function useTabsState() {
|
||||
@@ -33,9 +31,7 @@ function useTabsState() {
|
||||
|
||||
const setTabsState = useCallback((updater: (previous: TabsState) => TabsState) => {
|
||||
globalTabsState = updater(globalTabsState);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem('@gitbook/tabsState', JSON.stringify(globalTabsState));
|
||||
}
|
||||
storage.setItem('@gitbook/tabsState', globalTabsState);
|
||||
listeners.forEach((listener) => listener());
|
||||
}, []);
|
||||
const state = React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import * as storage from '@/lib/local-storage';
|
||||
|
||||
import { generateRandomId } from './utils';
|
||||
|
||||
const SESSION_TTL = 1000 * 60 * 30; // 30 minutes
|
||||
@@ -22,22 +24,20 @@ export function getSession(): Session {
|
||||
}
|
||||
|
||||
try {
|
||||
const rawSession =
|
||||
typeof localStorage !== 'undefined' ? localStorage.getItem(SESSION_KEY) : null;
|
||||
const session = storage.getItem<unknown | null>(SESSION_KEY, null);
|
||||
|
||||
if (rawSession) {
|
||||
const storedSession = JSON.parse(rawSession);
|
||||
|
||||
if (
|
||||
typeof storedSession === 'object' &&
|
||||
typeof storedSession.lastActiveAt === 'number' &&
|
||||
typeof storedSession.id === 'string' &&
|
||||
storedSession.lastActiveAt + SESSION_TTL > Date.now()
|
||||
) {
|
||||
currentSession = storedSession as Session;
|
||||
touchSession();
|
||||
return currentSession;
|
||||
}
|
||||
if (
|
||||
session &&
|
||||
typeof session === 'object' &&
|
||||
'lastActiveAt' in session &&
|
||||
typeof session.lastActiveAt === 'number' &&
|
||||
'id' in session &&
|
||||
typeof session.id === 'string' &&
|
||||
session.lastActiveAt + SESSION_TTL > Date.now()
|
||||
) {
|
||||
currentSession = session as Session;
|
||||
touchSession();
|
||||
return currentSession;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing session', error);
|
||||
@@ -65,7 +65,7 @@ export function touchSession() {
|
||||
* Save the session to the local storage.
|
||||
*/
|
||||
export function saveSession() {
|
||||
if (typeof localStorage !== 'undefined' && currentSession) {
|
||||
localStorage.setItem(SESSION_KEY, JSON.stringify(currentSession));
|
||||
if (currentSession) {
|
||||
storage.setItem(SESSION_KEY, currentSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Get an item from local storage safely.
|
||||
*/
|
||||
export function getItem<T>(key: string, defaultValue: T): T {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
const stored = localStorage.getItem(key);
|
||||
return stored ? (JSON.parse(stored) as T) : defaultValue;
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.name === 'SecurityError') {
|
||||
return defaultValue;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an item in local storage safely.
|
||||
*/
|
||||
export function setItem(key: string, value: unknown) {
|
||||
if (typeof localStorage === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.name === 'SecurityError') {
|
||||
return;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user