Skip to content

Commit

Permalink
feature(unlock-app): Improve localStorage usage (#15361)
Browse files Browse the repository at this point in the history
* Revert "refactor(unlock-app): simplified our use of localStorage (#15288)"

This reverts commit 34ca5e8.

* update app storage hook

* update session util

* update terms of service hook

* update terms of service hook test

* update terms of service hook

* update terms of service hook leniency
  • Loading branch information
0xTxbi authored Jan 8, 2025
1 parent babbb18 commit 44a3990
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 48 deletions.
3 changes: 3 additions & 0 deletions unlock-app/src/components/content/DemoContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ const usePaywall = () => {
},
}

// Remove localStorage (on the demo we do not want to store any account)
localStorage.removeItem('userInfo')

// Event handler
const handler = window.addEventListener(
'unlockProtocol.status',
Expand Down
55 changes: 36 additions & 19 deletions unlock-app/src/hooks/useAppStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,75 @@ import { useCallback } from 'react'

export const APP_NAME = '@unlock-app'

const isObject = (value: any) => typeof value === 'object'
/**
* Store objects as JSON, so it's easier to just always try parse, then fallback to the raw value on parse errors.
*/
function parseIfNeeded(value: string) {
try {
return JSON.parse(value)
} catch {
// If not valid JSON, just return the original string
return value
}
}

const getKey = (key: string, withAppName = true) => {
function getKey(key: string, withAppName = true) {
return withAppName ? `${APP_NAME}.${key}` : key
}

export const getLocalStorageItem = (key: string): string | null => {
export function getLocalStorageItem(key: string, withAppName = true) {
if (typeof window === 'undefined') return null
try {
const value = localStorage.getItem(getKey(key))
if (!value) return null
return isObject(value) ? JSON.parse(value) : value
const stored = localStorage.getItem(getKey(key, withAppName))
if (!stored) return null
return parseIfNeeded(stored)
} catch (error) {
console.error(error)
return null
}
return null
}

export const setLocalStorageItem = (key: string, value: any) => {
const currentValue = getLocalStorageItem(key)
if (currentValue === value) return false
export function setLocalStorageItem(
key: string,
value: any,
withAppName = true
) {
try {
localStorage.setItem(
getKey(key),
isObject(value) ? JSON.stringify(value) : value
getKey(key, withAppName),
typeof value === 'object' ? JSON.stringify(value) : String(value)
)
return value
} catch (error) {
console.error(error)
return null
}
return value
}

export const deleteLocalStorageItem = (key: string, withAppName = true) => {
export function deleteLocalStorageItem(key: string, withAppName = true) {
try {
localStorage.removeItem(getKey(key, withAppName))
} catch (error) {
console.error(error)
}
}

export const clearLocalStorage = (clearItems: string[], addAppName = true) => {
for (const item of clearItems) {
deleteLocalStorageItem(item, addAppName)
export function clearLocalStorage(keys: string[], withAppName = true) {
for (const key of keys) {
deleteLocalStorageItem(key, withAppName)
}
}

export function useAppStorage() {
// Get and set items in local storage, with caching!
const getStorage = useCallback(getLocalStorageItem, [])
const setStorage = useCallback(setLocalStorageItem, [])
const removeKey = useCallback(deleteLocalStorageItem, [])
const clearStorage = useCallback(clearLocalStorage, [])

return {
setStorage,
getStorage,
setStorage,
removeKey,
clearStorage,
}
}
26 changes: 10 additions & 16 deletions unlock-app/src/hooks/useTermsOfService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,29 @@ import { useAppStorage } from './useAppStorage'

export const localStorageKey = 'terms-of-service'

/**
* This hook retrieves metadata for a token
* @param {*} address
*/
export const useTermsOfService = () => {
const [termsLoading, setTermsLoading] = useState(true)
const [termsAccepted, setTermsAccepted] = useState(false)
const { getStorage, setStorage } = useAppStorage()

useEffect(() => {
const readFromLocalStorage = () => {
try {
setTermsAccepted(getStorage(localStorageKey) === 'true')
setTermsLoading(false)
} catch (error) {
// No localstorage, assume false!
setTermsAccepted(false)
setTermsLoading(false)
}
try {
const storedVal = getStorage(localStorageKey)
setTermsAccepted(storedVal === true)
} catch (error) {
console.error(error)
setTermsAccepted(false)
} finally {
setTermsLoading(false)
}
readFromLocalStorage()
}, [getStorage])

const saveTermsAccepted = () => {
setTermsAccepted(true)
try {
setStorage(localStorageKey, 'true')
setStorage(localStorageKey, true)
} catch (error) {
// Could not store in localstorage.
console.error('Could not store TOS', error)
}
}

Expand Down
28 changes: 15 additions & 13 deletions unlock-app/src/utils/session.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
import {
APP_NAME,
deleteLocalStorageItem,
getLocalStorageItem,
setLocalStorageItem,
deleteLocalStorageItem,
} from '~/hooks/useAppStorage'

export const CURRENT_ACCOUNT_KEY = `${APP_NAME}.account`
/**
* These constants are *not* prefixed with "@unlock-app."
* They'll be prefixed automatically.
*/
const CURRENT_ACCOUNT_KEY = 'account'
const PROVIDER_KEY = 'provider'
const NETWORK_KEY = 'network'

export const getSessionKey = (address: string) =>
`${APP_NAME}.session_${address.trim().toLowerCase()}`
`session_${address.trim().toLowerCase()}`

export const getCurrentAccount = () => {
return getLocalStorageItem(CURRENT_ACCOUNT_KEY) || undefined
}

export const getCurrentProvider = () => {
return getLocalStorageItem(`${APP_NAME}.provider`)
return getLocalStorageItem(PROVIDER_KEY)
}

export const getCurrentNetwork = () => {
const network = getLocalStorageItem(`${APP_NAME}.network`)
const network = getLocalStorageItem(NETWORK_KEY)
return network ? parseInt(network) : undefined
}

Expand All @@ -29,25 +34,22 @@ export const getAccessToken = (
if (!address) {
return null
}
const ACCESS_TOKEN_KEY = getSessionKey(address)
return getLocalStorageItem(ACCESS_TOKEN_KEY)
return getLocalStorageItem(getSessionKey(address))
}

export const removeAccessToken = (
address: string | undefined = getCurrentAccount()
) => {
if (!address) {
return null
return
}
const ACCESS_TOKEN_KEY = getSessionKey(address)
deleteLocalStorageItem(ACCESS_TOKEN_KEY)
deleteLocalStorageItem(getSessionKey(address))
}

export const saveAccessToken = ({
walletAddress,
accessToken,
}: Record<'walletAddress' | 'accessToken', string>) => {
const ACCESS_TOKEN_KEY = getSessionKey(walletAddress)
setLocalStorageItem(ACCESS_TOKEN_KEY, accessToken)
setLocalStorageItem(getSessionKey(walletAddress), accessToken)
return accessToken
}

0 comments on commit 44a3990

Please sign in to comment.