refactor: destruct config helper

This commit is contained in:
EnixCoda 2021-05-24 20:51:07 +08:00
parent 31c45781a0
commit ab82cf783e
No known key found for this signature in database
GPG key ID: 0C1A07377913A1DD
12 changed files with 211 additions and 188 deletions

View file

@ -1,6 +1,6 @@
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import { Field } from './settings/Field'
export type SimpleField<Key extends keyof Config> = {

View file

@ -2,7 +2,7 @@ import { wikiLinks } from 'components/settings/SettingsBar'
import { SimpleToggleField } from 'components/SimpleToggleField'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import { Option, SelectInput } from '../SelectInput'
import { Field } from './Field'
import { SettingsSection } from './SettingsSection'

View file

@ -3,7 +3,7 @@ import { Option, SelectInput } from 'components/SelectInput'
import { SimpleToggleField } from 'components/SimpleToggleField'
import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import { friendlyFormatShortcut } from 'utils/general'
import { useStateIO } from 'utils/hooks/useStateIO'
import * as keyHelper from 'utils/keyHelper'

View file

@ -1,6 +1,5 @@
import * as React from 'react'
import * as configsHelper from 'utils/configHelper'
import { Config } from 'utils/configHelper'
import { Config, configHelper } from 'utils/config/helper'
type Props = {}
@ -12,12 +11,12 @@ export const ConfigsContext = React.createContext<ContextShape | null>(null)
export function ConfigsContextWrapper(props: React.PropsWithChildren<Props>) {
const [configs, setConfigs] = React.useState<Config | null>(null)
React.useEffect(() => {
configsHelper.get().then(setConfigs)
configHelper.get().then(setConfigs)
}, [])
const onChange = React.useCallback(
(updatedConfigs: Partial<Config>) => {
const mergedConfigs = { ...configs, ...updatedConfigs } as Config
configsHelper.set(mergedConfigs)
configHelper.set(mergedConfigs)
setConfigs(mergedConfigs)
},
[configs, setConfigs],

View file

@ -1,7 +1,7 @@
import { SideBarStateContextShape } from 'components/SideBarState'
import { GetCreatedMethod, MethodCreator } from 'driver/connect'
import { platform } from 'platforms'
import { Config } from 'utils/configHelper'
import { Config } from 'utils/config/helper'
import * as DOMHelper from 'utils/DOMHelper'
import { VisibleNodes, VisibleNodesGenerator } from 'utils/VisibleNodesGenerator'

View file

@ -0,0 +1,81 @@
import { SearchMode } from 'components/searchModes'
import { storageHelper } from 'utils/storageHelper'
import { migrateConfig } from './migrations'
export type Config = {
sideBarWidth: number
shortcut: string | undefined
accessToken: string | undefined
compressSingletonFolder: boolean
copyFileButton: boolean
copySnippetButton: boolean
intelligentToggle: boolean | null // `null` stands for intelligent, boolean for sidebar open state
icons: 'rich' | 'dim' | 'native'
toggleButtonVerticalDistance: number
toggleButtonContent: 'logo' | 'octoface'
recursiveToggleFolder: 'shift' | 'alt'
searchMode: SearchMode
sidebarToggleMode: 'persistent' | 'float'
}
enum configKeys {
sideBarWidth = 'sideBarWidth',
shortcut = 'shortcut',
accessToken = 'accessToken',
compressSingletonFolder = 'compressSingletonFolder',
copyFileButton = 'copyFileButton',
copySnippetButton = 'copySnippetButton',
intelligentToggle = 'intelligentToggle',
icons = 'icons',
toggleButtonVerticalDistance = 'toggleButtonVerticalDistance',
toggleButtonContent = 'toggleButtonContent',
recursiveToggleFolder = 'recursiveToggleFolder',
searchMode = 'searchMode',
sidebarToggleMode = 'sidebarToggleMode',
}
const defaultConfigs: Config = {
sideBarWidth: 260,
shortcut: undefined,
accessToken: '',
compressSingletonFolder: true,
copyFileButton: true,
copySnippetButton: true,
intelligentToggle: null,
icons: 'rich',
toggleButtonVerticalDistance: 124, // align with GitHub's navbar items
toggleButtonContent: 'logo',
recursiveToggleFolder: 'shift',
searchMode: 'fuzzy',
sidebarToggleMode: 'float',
}
const configKeyArray = Object.values(configKeys)
function applyDefaultConfigs(configs: Partial<Config>) {
return configKeyArray.reduce((applied, key) => {
Object.assign(applied, { [key]: key in configs ? configs[key] : defaultConfigs[key] })
return applied
}, {} as Config)
}
export type VersionedConfig<SiteConfig> = Record<string, SiteConfig> & { configVersion: string }
// do NOT use platform name
const platformStorageKey = `platform_` + window.location.host.toLowerCase()
const prepareConfig = new Promise<void>(async resolve => {
await migrateConfig()
resolve()
})
async function get(): Promise<Config> {
await prepareConfig
const config = await storageHelper.get<Record<string, Config>>([platformStorageKey])
return applyDefaultConfigs(config?.[platformStorageKey] || {})
}
async function set(config: Config) {
return await storageHelper.set({ [platformStorageKey]: config })
}
export const configHelper = { get, set }

View file

@ -0,0 +1,23 @@
import { storageHelper } from 'utils/storageHelper'
import { Migration } from '.'
import { Storage } from '../../storageHelper'
export const migration: Migration = {
version: '1.0.1',
async migrate(version) {
const config: any | void = await storageHelper.get<Storage>([
'configVersion',
'sideBarWidth',
'shortcut',
'access_token',
'compressSingletonFolder',
'copyFileButton',
'copySnippetButton',
'intelligentToggle',
'icons',
])
if (config && (!('configVersion' in config) || config.configVersion < version)) {
await storageHelper.set({ platform_GitHub: config, configVersion: version })
}
},
}

View file

@ -0,0 +1,28 @@
import { storageHelper } from 'utils/storageHelper'
import { Migration } from '.'
import { Storage } from '../../storageHelper'
import { Config, VersionedConfig } from '../helper'
export const migration: Migration = {
version: '1.3.4',
async migrate(version) {
const config: any | void = await storageHelper.get<VersionedConfig<Config> & Storage>([
'configVersion',
'platform_undefined',
'platform_GitHub',
'platform_github.com',
])
if (
config &&
'configVersion' in config &&
config.configVersion < version &&
(config.platform_GitHub || config.platform_undefined) &&
!config['platform_github.com']
) {
await storageHelper.set({
['platform_github.com']: config.platform_GitHub || config.platform_undefined,
configVersion: version,
})
}
},
}

View file

@ -0,0 +1,41 @@
import { storageHelper } from 'utils/storageHelper'
import { Migration } from '.'
import { Storage } from '../../storageHelper'
import { VersionedConfig } from '../helper'
export const migration: Migration = {
version: '2.6.0',
async migrate(version) {
type ConfigBeforeMigrate = {
access_token?: string
}
type ConfigAfterMigrate = {
accessToken?: string
}
const config = await storageHelper.get<VersionedConfig<ConfigBeforeMigrate> & Storage>()
if (config && config.configVersion < version) {
const { configVersion, ...restConfig } = config
for (const key of Object.keys(restConfig)) {
if (
typeof restConfig[key] === 'object' &&
restConfig[key] &&
'access_token' in restConfig[key]
) {
const config: ConfigBeforeMigrate = restConfig[key]
const { access_token: accessToken, ...legacy } = config
const migrated: ConfigAfterMigrate = {
...legacy,
accessToken,
}
await storageHelper.set({
[key]: migrated,
})
}
}
await storageHelper.set({
configVersion: version,
})
}
},
}

View file

@ -0,0 +1,18 @@
import { storageHelper } from 'utils/storageHelper'
import { Storage } from '../../storageHelper'
import { migration as v1v0v1 } from './1.0.1'
import { migration as v1v3v4 } from './1.3.4'
import { migration as v2v6v0 } from './2.6.0'
export type Migration = {
version: string
migrate(version: Migration['version']): Async<void>
}
export async function migrateConfig() {
const migrations: Migration[] = [v1v0v1, v1v3v4, v2v6v0]
for (const { version, migrate } of migrations) {
await migrate(version)
}
}

View file

@ -1,178 +0,0 @@
import { SearchMode } from 'components/searchModes'
import * as storageHelper from 'utils/storageHelper'
export type Config = {
sideBarWidth: number
shortcut: string | undefined
accessToken: string | undefined
compressSingletonFolder: boolean
copyFileButton: boolean
copySnippetButton: boolean
intelligentToggle: boolean | null // `null` stands for intelligent, boolean for sidebar open state
icons: 'rich' | 'dim' | 'native'
toggleButtonVerticalDistance: number
toggleButtonContent: 'logo' | 'octoface'
recursiveToggleFolder: 'shift' | 'alt'
searchMode: SearchMode
sidebarToggleMode: 'persistent' | 'float'
}
enum configKeys {
sideBarWidth = 'sideBarWidth',
shortcut = 'shortcut',
accessToken = 'accessToken',
compressSingletonFolder = 'compressSingletonFolder',
copyFileButton = 'copyFileButton',
copySnippetButton = 'copySnippetButton',
intelligentToggle = 'intelligentToggle',
icons = 'icons',
toggleButtonVerticalDistance = 'toggleButtonVerticalDistance',
toggleButtonContent = 'toggleButtonContent',
recursiveToggleFolder = 'recursiveToggleFolder',
searchMode = 'searchMode',
sidebarToggleMode = 'sidebarToggleMode',
}
const defaultConfigs: Config = {
sideBarWidth: 260,
shortcut: undefined,
accessToken: '',
compressSingletonFolder: true,
copyFileButton: true,
copySnippetButton: true,
intelligentToggle: null,
icons: 'rich',
toggleButtonVerticalDistance: 124, // align with GitHub's navbar items
toggleButtonContent: 'logo',
recursiveToggleFolder: 'shift',
searchMode: 'fuzzy',
sidebarToggleMode: 'float',
}
const configKeyArray = Object.values(configKeys)
function applyDefaultConfigs(configs: Partial<Config>) {
return configKeyArray.reduce((applied, key) => {
Object.assign(applied, { [key]: key in configs ? configs[key] : defaultConfigs[key] })
return applied
}, {} as Config)
}
type VersionedConfig<SiteConfig> = Record<string, SiteConfig> & { configVersion: string }
type Storage = {
// save root level `configVersion` for easier future migrating
[key in 'configVersion' | string]: string
// separate different platform configs to simplify interactions with browser storage API
// e.g.
// platform_github.com?: Config
}
async function migrateConfig() {
// not referencing to enum above to prevent migrate future configs
const migrations: {
version: string
migrate(version: string): Promise<void>
}[] = [
{
version: '1.0.1',
async migrate(version) {
const config: any | void = await storageHelper.get<Storage>([
'configVersion',
'sideBarWidth',
'shortcut',
'access_token',
'compressSingletonFolder',
'copyFileButton',
'copySnippetButton',
'intelligentToggle',
'icons',
])
if (config && (!('configVersion' in config) || config.configVersion < version)) {
await storageHelper.set({ platform_GitHub: config, configVersion: version })
}
},
},
{
version: '1.3.4',
async migrate(version) {
const config: any | void = await storageHelper.get<VersionedConfig<Config> & Storage>([
'configVersion',
'platform_undefined', // this was a mistake :(
'platform_GitHub',
'platform_github.com',
])
if (
config &&
'configVersion' in config &&
config.configVersion < version &&
(config.platform_GitHub || config.platform_undefined) &&
!config['platform_github.com']
) {
await storageHelper.set({
['platform_github.com']: config.platform_GitHub || config.platform_undefined,
configVersion: version,
})
}
},
},
{
version: '2.6.0',
async migrate(version) {
type LegacySiteConfig = {
access_token?: string
}
type MigratedSiteConfig = {
accessToken?: string
}
const config = await storageHelper.get<VersionedConfig<LegacySiteConfig> & Storage>()
if (config && config.configVersion < version) {
const { configVersion, ...restConfig } = config
for (const key of Object.keys(restConfig)) {
if (
typeof restConfig[key] === 'object' &&
restConfig[key] &&
'access_token' in restConfig[key]
) {
const config: LegacySiteConfig = restConfig[key]
const { access_token: accessToken, ...legacy } = config
const migrated: MigratedSiteConfig = {
...legacy,
accessToken,
}
await storageHelper.set({
[key]: migrated,
})
}
}
await storageHelper.set({
configVersion: version,
})
}
},
},
]
for (const { version, migrate } of migrations) {
await migrate(version)
}
}
// do NOT use platform name
const platformStorageKey = `platform_` + window.location.host.toLowerCase()
const prepareConfig = new Promise<void>(async resolve => {
await migrateConfig()
resolve()
})
export async function get(): Promise<Config> {
await prepareConfig
const config = await storageHelper.get<Record<string, Config>>([platformStorageKey])
return applyDefaultConfigs(config?.[platformStorageKey] || {})
}
export async function set(config: Config) {
return await storageHelper.set({ [platformStorageKey]: config })
}

View file

@ -1,6 +1,15 @@
const localStorage = browser.storage.local
export async function get<
export type Storage = {
// save root level `configVersion` for easier future migrating
[key in EnumString<'configVersion'>]: string
// separate different platform configs to simplify interactions with browser storage API
// e.g.
// ['platform_github.com']?: Config
}
async function get<
T extends {
[key: string]: any
}
@ -10,8 +19,10 @@ export async function get<
} catch (err) {}
}
export function set(value: any): Promise<void> | void {
function set(value: any): Promise<void> | void {
try {
return localStorage.set(value)
} catch (err) {}
}
export const storageHelper = { get, set }