Skip to content

Commit aab3c00

Browse files
authored
fix: dont use local storage for text config items (#448)
1 parent 8c237e5 commit aab3c00

File tree

7 files changed

+190
-165
lines changed

7 files changed

+190
-165
lines changed

src/components/local-storage-input.tsx

Lines changed: 0 additions & 90 deletions
This file was deleted.

src/components/textarea-input.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React, { useCallback, useEffect, useState } from 'react'
2+
import { InputDescription } from './input-description.js'
3+
import { InputLabel } from './input-label.js'
4+
5+
export interface InputProps extends Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, 'onChange'> {
6+
label: string
7+
placeholder?: string
8+
value: string
9+
validationFn?(value: string): Error | null
10+
resetKey: number
11+
description?: string
12+
preSaveFormat?(value: string): any
13+
onChange(value: string): void
14+
}
15+
16+
export default ({ resetKey, onChange, label, placeholder, validationFn, value, description, preSaveFormat, ...props }: InputProps): JSX.Element => {
17+
const [internalValue, setInternalValue] = useState(value)
18+
const [error, setError] = useState<null | Error>(null)
19+
20+
useEffect(() => {
21+
setInternalValue(value)
22+
}, [resetKey, value])
23+
24+
const updateValue = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
25+
setInternalValue(e.target.value)
26+
}, [])
27+
28+
const saveValue = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
29+
const newValue = e.target.value
30+
try {
31+
const err = validationFn?.(newValue)
32+
if (err != null) {
33+
throw err
34+
}
35+
setInternalValue(newValue)
36+
37+
onChange(preSaveFormat?.(newValue) ?? newValue)
38+
setError(null)
39+
} catch (err) {
40+
setError(err as Error)
41+
}
42+
}, [onChange, preSaveFormat, validationFn])
43+
44+
props = {
45+
...props,
46+
className: `${props.className ?? ''} flex-column items-start`
47+
}
48+
49+
return (
50+
<div {...props}>
51+
<InputLabel label={label} />
52+
<InputDescription description={description} />
53+
<textarea
54+
className='input-reset ba br2 b--light-silver code lh-copy black-80 bg-white pa2 w-100 mt2'
55+
id={label}
56+
name={label}
57+
placeholder={placeholder}
58+
value={internalValue}
59+
onChange={updateValue}
60+
onBlur={saveValue}
61+
/>
62+
{error != null && <span className='db lh-copy red pt1 tr f6 w-100'>{error.message}</span>}
63+
</div>
64+
)
65+
}

src/context/config-context.tsx

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,92 @@
1-
import React, { createContext, useState } from 'react'
1+
import React, { createContext, useCallback, useEffect, useState } from 'react'
2+
import { getConfig, type ConfigDb } from '../lib/config-db.js'
23
import { isConfigPage } from '../lib/is-config-page.js'
4+
import { getUiComponentLogger } from '../lib/logger.js'
35

46
const isLoadedInIframe = window.self !== window.top
57

6-
export const ConfigContext = createContext({
8+
type ConfigKey = keyof ConfigDb
9+
export interface ConfigContextType extends ConfigDb {
10+
isConfigExpanded: boolean
11+
setConfigExpanded(value: boolean): void
12+
setConfig(key: ConfigKey, value: any): void
13+
}
14+
15+
export const ConfigContext = createContext<ConfigContextType>({
716
isConfigExpanded: isLoadedInIframe,
8-
setConfigExpanded: (value: boolean) => {}
17+
setConfigExpanded: (value: boolean) => {},
18+
setConfig: (key, value) => {},
19+
gateways: [],
20+
routers: [],
21+
dnsJsonResolvers: {},
22+
enableWss: false,
23+
enableWebTransport: false,
24+
enableGatewayProviders: false,
25+
enableRecursiveGateways: false,
26+
debug: ''
927
})
1028

1129
export const ConfigProvider = ({ children }: { children: JSX.Element[] | JSX.Element, expanded?: boolean }): JSX.Element => {
1230
const [isConfigExpanded, setConfigExpanded] = useState(isConfigPage(window.location.hash))
31+
const [gateways, setGateways] = useState<string[]>([])
32+
const [routers, setRouters] = useState<string[]>([])
33+
const [dnsJsonResolvers, setDnsJsonResolvers] = useState<Record<string, string>>({})
34+
const [enableWss, setEnableWss] = useState(false)
35+
const [enableWebTransport, setEnableWebTransport] = useState(false)
36+
const [enableGatewayProviders, setEnableGatewayProviders] = useState(false)
37+
const [enableRecursiveGateways, setEnableRecursiveGateways] = useState(false)
38+
const [debug, setDebug] = useState('')
1339
const isExplicitlyLoadedConfigPage = isConfigPage(window.location.hash)
40+
/**
41+
* We need to make sure that the configDb types are loaded with the values from IDB
42+
*/
43+
useEffect(() => {
44+
void (async () => {
45+
const config = await getConfig(getUiComponentLogger('config-context'))
46+
setGateways(config.gateways)
47+
setRouters(config.routers)
48+
setDnsJsonResolvers(config.dnsJsonResolvers)
49+
setEnableWss(config.enableWss)
50+
setEnableWebTransport(config.enableWebTransport)
51+
setEnableGatewayProviders(config.enableGatewayProviders)
52+
setEnableRecursiveGateways(config.enableRecursiveGateways)
53+
setDebug(config.debug)
54+
})()
55+
}, [])
56+
57+
/**
58+
* Sets the config values for the context provider. To save to IDB, use the `setConfig` function from `lib/config-db.ts`.
59+
*/
60+
const setConfigLocal = useCallback((key: ConfigKey, value: any) => {
61+
switch (key) {
62+
case 'gateways':
63+
setGateways(value)
64+
break
65+
case 'routers':
66+
setRouters(value)
67+
break
68+
case 'dnsJsonResolvers':
69+
setDnsJsonResolvers(value)
70+
break
71+
case 'enableWss':
72+
setEnableWss(value)
73+
break
74+
case 'enableWebTransport':
75+
setEnableWebTransport(value)
76+
break
77+
case 'enableGatewayProviders':
78+
setEnableGatewayProviders(value)
79+
break
80+
case 'enableRecursiveGateways':
81+
setEnableRecursiveGateways(value)
82+
break
83+
case 'debug':
84+
setDebug(value)
85+
break
86+
default:
87+
throw new Error(`Unknown config key: ${key}`)
88+
}
89+
}, [])
1490

1591
const setConfigExpandedWrapped = (value: boolean): void => {
1692
if (isLoadedInIframe || isExplicitlyLoadedConfigPage) {
@@ -21,7 +97,7 @@ export const ConfigProvider = ({ children }: { children: JSX.Element[] | JSX.Ele
2197
}
2298

2399
return (
24-
<ConfigContext.Provider value={{ isConfigExpanded, setConfigExpanded: setConfigExpandedWrapped }}>
100+
<ConfigContext.Provider value={{ isConfigExpanded, setConfigExpanded: setConfigExpandedWrapped, setConfig: setConfigLocal, gateways, routers, dnsJsonResolvers, enableWss, enableWebTransport, enableGatewayProviders, enableRecursiveGateways, debug }}>
25101
{children}
26102
</ConfigContext.Provider>
27103
)

src/lib/config-db.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,14 @@ export const defaultEnableGatewayProviders = true
2525

2626
const configDb = new GenericIDB<ConfigDb>('helia-sw', 'config')
2727

28-
export async function localStorageToIdb (): Promise<void> {
28+
export async function localStorageToIdb ({ gateways, routers, dnsJsonResolvers, debug }: Pick<ConfigDb, 'gateways' | 'routers' | 'dnsJsonResolvers' | 'debug'>): Promise<void> {
2929
if (typeof globalThis.localStorage !== 'undefined') {
3030
await configDb.open()
3131
const localStorage = globalThis.localStorage
32-
const localStorageGatewaysString = localStorage.getItem(LOCAL_STORAGE_KEYS.config.gateways) ?? JSON.stringify(defaultGateways)
33-
const localStorageRoutersString = localStorage.getItem(LOCAL_STORAGE_KEYS.config.routers) ?? JSON.stringify(defaultRouters)
34-
const localStorageDnsResolvers = localStorage.getItem(LOCAL_STORAGE_KEYS.config.dnsJsonResolvers) ?? JSON.stringify(defaultDnsJsonResolvers)
3532
const enableRecursiveGateways = localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableRecursiveGateways) === null ? defaultEnableRecursiveGateways : localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableRecursiveGateways) === 'true'
3633
const enableWss = localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableWss) === null ? defaultEnableWss : localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableWss) === 'true'
3734
const enableWebTransport = localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableWebTransport) === null ? defaultEnableWebTransport : localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableWebTransport) === 'true'
3835
const enableGatewayProviders = localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableGatewayProviders) === null ? defaultEnableGatewayProviders : localStorage.getItem(LOCAL_STORAGE_KEYS.config.enableGatewayProviders) === 'true'
39-
const debug = localStorage.getItem(LOCAL_STORAGE_KEYS.config.debug) ?? ''
40-
const gateways = JSON.parse(localStorageGatewaysString)
41-
const routers = JSON.parse(localStorageRoutersString)
42-
const dnsJsonResolvers = JSON.parse(localStorageDnsResolvers)
4336
enable(debug)
4437

4538
await configDb.put('gateways', gateways)
@@ -56,11 +49,8 @@ export async function localStorageToIdb (): Promise<void> {
5649

5750
export async function resetConfig (): Promise<void> {
5851
await configDb.open()
59-
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.gateways)
6052
await configDb.put('gateways', defaultGateways)
61-
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.routers)
6253
await configDb.put('routers', defaultRouters)
63-
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.dnsJsonResolvers)
6454
await configDb.put('dnsJsonResolvers', defaultDnsJsonResolvers)
6555
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.enableWss)
6656
await configDb.put('enableWss', defaultEnableWss)
@@ -70,7 +60,6 @@ export async function resetConfig (): Promise<void> {
7060
await configDb.put('enableRecursiveGateways', defaultEnableRecursiveGateways)
7161
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.enableGatewayProviders)
7262
await configDb.put('enableGatewayProviders', defaultEnableGatewayProviders)
73-
localStorage.removeItem(LOCAL_STORAGE_KEYS.config.debug)
7463
await configDb.put('debug', '')
7564
configDb.close()
7665
}

src/lib/input-helpers.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const convertUrlArrayToInput = (urls: string[]): string => {
2+
return urls.join('\n')
3+
}
4+
5+
export const convertUrlInputToArray = (newlineDelimitedString: string): string[] => {
6+
return newlineDelimitedString.length > 0 ? newlineDelimitedString.split('\n').map((u) => u.trim()) : []
7+
}
8+
9+
export const convertDnsResolverObjectToInput = (dnsResolvers: Record<string, string>): string => {
10+
return Object.entries(dnsResolvers).map(([key, url]) => `${key} ${url}`).join('\n')
11+
}
12+
13+
export const convertDnsResolverInputToObject = (dnsResolverInput: string): Record<string, string> => {
14+
return dnsResolverInput.split('\n').map((u) => u.trim().split(' ')).reduce((acc, [key, url]) => {
15+
acc[key] = url
16+
return acc
17+
}, {})
18+
}

src/lib/local-storage.ts

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,12 @@ export function getLocalStorageKey (root: LocalStorageRoots, key: string): strin
66

77
export const LOCAL_STORAGE_KEYS = {
88
config: {
9-
gateways: getLocalStorageKey('config', 'gateways'),
10-
routers: getLocalStorageKey('config', 'routers'),
119
enableWss: getLocalStorageKey('config', 'enableWss'),
1210
enableWebTransport: getLocalStorageKey('config', 'enableWebTransport'),
1311
enableRecursiveGateways: getLocalStorageKey('config', 'enableRecursiveGateways'),
14-
enableGatewayProviders: getLocalStorageKey('config', 'enableGatewayProviders'),
15-
dnsJsonResolvers: getLocalStorageKey('config', 'dnsJsonResolvers'),
16-
debug: getLocalStorageKey('config', 'debug')
12+
enableGatewayProviders: getLocalStorageKey('config', 'enableGatewayProviders')
1713
},
1814
forms: {
1915
requestPath: getLocalStorageKey('forms', 'requestPath')
2016
}
2117
}
22-
23-
export const convertUrlArrayToInput = (urls: string[]): string => {
24-
return urls.join('\n')
25-
}
26-
27-
export const convertUrlInputToArray = (newlineDelimitedString: string): string[] => {
28-
return newlineDelimitedString.length > 0 ? newlineDelimitedString.split('\n').map((u) => u.trim()) : []
29-
}
30-
31-
export const convertDnsResolverObjectToInput = (dnsResolvers: Record<string, string>): string => {
32-
return Object.entries(dnsResolvers).map(([key, url]) => `${key} ${url}`).join('\n')
33-
}
34-
35-
export const convertDnsResolverInputToObject = (dnsResolverInput: string): Record<string, string> => {
36-
return dnsResolverInput.split('\n').map((u) => u.trim().split(' ')).reduce((acc, [key, url]) => {
37-
acc[key] = url
38-
return acc
39-
}, {})
40-
}

0 commit comments

Comments
 (0)