Protect local storage calls (#3043)
This PR provides some safe wrappers for local storage calls. Local storage is not available in all environments (for example, a React Native web view). The PR also adds an eslint rule preventing direct calls to local / session storage. ### Change Type - [x] `patch` — Bug fix ### Release Notes - Fixes a bug that could cause crashes in React Native webviews.
This commit is contained in:
parent
3161e5cb4f
commit
2f28d7c6f8
18 changed files with 625 additions and 52 deletions
|
@ -1,17 +1,15 @@
|
|||
/**
|
||||
* What is going on in this file?
|
||||
*
|
||||
* We had some bad early assumptions about how we would store documents.
|
||||
* Which ended up with us generating random persistenceKey strings for the
|
||||
* 'scratch' document for each user (i.e. each browser context), and storing it in localStorage.
|
||||
*
|
||||
* Many users still have that random string in their localStorage so we need to load it. But for new
|
||||
* users it does not need to be unique and we can just use a constant.
|
||||
*/
|
||||
// What is going on in this file?
|
||||
// We had some bad early assumptions about how we would store documents.
|
||||
// Which ended up with us generating random persistenceKey strings for the
|
||||
// 'scratch' document for each user (i.e. each browser context), and storing it in localStorage.
|
||||
// Many users still have that random string in their localStorage so we need to load it. But for new
|
||||
// users it does not need to be unique and we can just use a constant.
|
||||
|
||||
import { getFromLocalStorage, setInLocalStorage } from 'tldraw'
|
||||
|
||||
// DO NOT CHANGE THESE WITHOUT ADDING MIGRATION LOGIC. DOING SO WOULD WIPE ALL EXISTING LOCAL DATA.
|
||||
const defaultDocumentKey = 'TLDRAW_DEFAULT_DOCUMENT_NAME_v2'
|
||||
const w = typeof window === 'undefined' ? undefined : window
|
||||
|
||||
export const SCRATCH_PERSISTENCE_KEY =
|
||||
(w?.localStorage.getItem(defaultDocumentKey) as any) ?? 'tldraw_document_v3'
|
||||
w?.localStorage.setItem(defaultDocumentKey, SCRATCH_PERSISTENCE_KEY)
|
||||
(getFromLocalStorage(defaultDocumentKey) as any) ?? 'tldraw_document_v3'
|
||||
setInLocalStorage(defaultDocumentKey, SCRATCH_PERSISTENCE_KEY)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { T, atom } from 'tldraw'
|
||||
import { T, atom, getFromLocalStorage, setInLocalStorage } from 'tldraw'
|
||||
|
||||
const channel =
|
||||
typeof BroadcastChannel !== 'undefined' ? new BroadcastChannel('tldrawUserPreferences') : null
|
||||
|
@ -37,9 +37,7 @@ function createPreference<Type>(key: string, validator: T.Validator<Type>, defau
|
|||
}
|
||||
|
||||
function loadItemFromStorage<Type>(key: string, validator: T.Validator<Type>): Type | null {
|
||||
if (typeof localStorage === 'undefined' || !localStorage) return null
|
||||
|
||||
const item = localStorage.getItem(`tldrawUserPreferences.${key}`)
|
||||
const item = getFromLocalStorage(`tldrawUserPreferences.${key}`, null)
|
||||
if (item == null) return null
|
||||
try {
|
||||
return validator.validate(JSON.parse(item))
|
||||
|
@ -49,11 +47,5 @@ function loadItemFromStorage<Type>(key: string, validator: T.Validator<Type>): T
|
|||
}
|
||||
|
||||
function saveItemToStorage(key: string, value: unknown): void {
|
||||
if (typeof localStorage === 'undefined' || !localStorage) return
|
||||
|
||||
try {
|
||||
localStorage.setItem(`tldrawUserPreferences.${key}`, JSON.stringify(value))
|
||||
} catch (e) {
|
||||
// not a big deal
|
||||
}
|
||||
setInLocalStorage(`tldrawUserPreferences.${key}`, JSON.stringify(value))
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue