refactor: storage
This commit is contained in:
4
src/common/constants.ts
Normal file
4
src/common/constants.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export const StorageKey = {
|
||||
THEME_SETTING: 'themeSetting',
|
||||
RELAY_GROUPS: 'relayGroups'
|
||||
}
|
||||
@@ -24,14 +24,13 @@ export type TElectronWindow = {
|
||||
isEncryptionAvailable: () => Promise<boolean>
|
||||
}
|
||||
theme: {
|
||||
onChange: (cb: (theme: TTheme) => void) => void
|
||||
addChangeListener: (listener: (theme: TTheme) => void) => void
|
||||
removeChangeListener: () => void
|
||||
current: () => Promise<TTheme>
|
||||
themeSetting: () => Promise<TThemeSetting>
|
||||
set: (themeSetting: TThemeSetting) => Promise<void>
|
||||
}
|
||||
storage: {
|
||||
getRelayGroups: () => Promise<TRelayGroup[]>
|
||||
setRelayGroups: (relayGroups: TRelayGroup[]) => Promise<void>
|
||||
getItem: (key: string) => Promise<string>
|
||||
setItem: (key: string, value: string) => Promise<void>
|
||||
}
|
||||
nostr: {
|
||||
login: (nsec: string) => Promise<{
|
||||
|
||||
@@ -71,7 +71,7 @@ app.whenReady().then(async () => {
|
||||
const storageService = new StorageService()
|
||||
storageService.init()
|
||||
|
||||
const themeService = new ThemeService(storageService, sendToRenderer)
|
||||
const themeService = new ThemeService(sendToRenderer)
|
||||
themeService.init()
|
||||
|
||||
const nostrService = new NostrService()
|
||||
|
||||
@@ -1,42 +1,10 @@
|
||||
import { TConfig, TRelayGroup, TThemeSetting } from '@common/types'
|
||||
import { app, ipcMain } from 'electron'
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
export class StorageService {
|
||||
private storage: Storage
|
||||
|
||||
constructor() {
|
||||
this.storage = new Storage()
|
||||
}
|
||||
|
||||
init() {
|
||||
ipcMain.handle('storage:getRelayGroups', () => this.getRelayGroups())
|
||||
ipcMain.handle('storage:setRelayGroups', (_, relayGroups: TRelayGroup[]) =>
|
||||
this.setRelayGroups(relayGroups)
|
||||
)
|
||||
}
|
||||
|
||||
getRelayGroups(): TRelayGroup[] | null {
|
||||
return this.storage.get('relayGroups') ?? null
|
||||
}
|
||||
|
||||
setRelayGroups(relayGroups: TRelayGroup[]) {
|
||||
this.storage.set('relayGroups', relayGroups)
|
||||
}
|
||||
|
||||
getTheme() {
|
||||
return this.storage.get('theme') ?? 'system'
|
||||
}
|
||||
|
||||
setTheme(theme: TThemeSetting) {
|
||||
this.storage.set('theme', theme)
|
||||
}
|
||||
}
|
||||
|
||||
class Storage {
|
||||
private path: string
|
||||
private config: TConfig
|
||||
private config: Record<string, string> = {}
|
||||
private writeTimer: NodeJS.Timeout | null = null
|
||||
|
||||
constructor() {
|
||||
@@ -46,11 +14,20 @@ class Storage {
|
||||
this.config = JSON.parse(json)
|
||||
}
|
||||
|
||||
get<K extends keyof TConfig, V extends TConfig[K]>(key: K): V | undefined {
|
||||
return this.config[key] as V
|
||||
init() {
|
||||
ipcMain.handle('storage:getItem', (_, key: string) => this.getItem(key))
|
||||
ipcMain.handle('storage:setItem', (_, key: string, value: string) => this.setItem(key, value))
|
||||
}
|
||||
|
||||
set<K extends keyof TConfig>(key: K, value: TConfig[K]) {
|
||||
getItem(key: string): string | undefined {
|
||||
const value = this.config[key]
|
||||
// backward compatibility
|
||||
if (value && typeof value !== 'string') return JSON.stringify(value)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
setItem(key: string, value: string) {
|
||||
this.config[key] = value
|
||||
if (this.writeTimer) return
|
||||
|
||||
|
||||
@@ -1,40 +1,18 @@
|
||||
import { TThemeSetting } from '@common/types'
|
||||
import { ipcMain, nativeTheme } from 'electron'
|
||||
import { TSendToRenderer } from '../types'
|
||||
import { StorageService } from './storage.service'
|
||||
|
||||
export class ThemeService {
|
||||
private themeSetting: TThemeSetting = 'system'
|
||||
|
||||
constructor(
|
||||
private storageService: StorageService,
|
||||
private sendToRenderer: TSendToRenderer
|
||||
) {}
|
||||
constructor(private sendToRenderer: TSendToRenderer) {}
|
||||
|
||||
init() {
|
||||
this.themeSetting = this.storageService.getTheme()
|
||||
|
||||
ipcMain.handle('theme:current', () => this.getCurrentTheme())
|
||||
ipcMain.handle('theme:themeSetting', () => this.themeSetting)
|
||||
ipcMain.handle('theme:set', (_, theme: TThemeSetting) => this.setTheme(theme))
|
||||
nativeTheme.on('updated', () => {
|
||||
if (this.themeSetting === 'system') {
|
||||
this.sendCurrentThemeToRenderer()
|
||||
}
|
||||
this.sendCurrentThemeToRenderer()
|
||||
})
|
||||
}
|
||||
|
||||
getCurrentTheme() {
|
||||
if (this.themeSetting === 'system') {
|
||||
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
|
||||
}
|
||||
return this.themeSetting
|
||||
}
|
||||
|
||||
private setTheme(theme: TThemeSetting) {
|
||||
this.themeSetting = theme
|
||||
this.storageService.setTheme(theme)
|
||||
this.sendCurrentThemeToRenderer()
|
||||
return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
private sendCurrentThemeToRenderer() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { TDraftEvent, TRelayGroup, TThemeSetting } from '@common/types'
|
||||
import { TDraftEvent, TTheme } from '@common/types'
|
||||
import { electronAPI } from '@electron-toolkit/preload'
|
||||
import { contextBridge, ipcRenderer } from 'electron'
|
||||
|
||||
@@ -8,19 +8,19 @@ const api = {
|
||||
isEncryptionAvailable: () => ipcRenderer.invoke('system:isEncryptionAvailable')
|
||||
},
|
||||
theme: {
|
||||
onChange: (cb: (theme: 'dark' | 'light') => void) => {
|
||||
addChangeListener: (listener: (theme: TTheme) => void) => {
|
||||
ipcRenderer.on('theme:change', (_, theme) => {
|
||||
cb(theme)
|
||||
listener(theme)
|
||||
})
|
||||
},
|
||||
current: () => ipcRenderer.invoke('theme:current'),
|
||||
themeSetting: () => ipcRenderer.invoke('theme:themeSetting'),
|
||||
set: (themeSetting: TThemeSetting) => ipcRenderer.invoke('theme:set', themeSetting)
|
||||
removeChangeListener: () => {
|
||||
ipcRenderer.removeAllListeners('theme:change')
|
||||
},
|
||||
current: () => ipcRenderer.invoke('theme:current')
|
||||
},
|
||||
storage: {
|
||||
getRelayGroups: () => ipcRenderer.invoke('storage:getRelayGroups'),
|
||||
setRelayGroups: (relayGroups: TRelayGroup[]) =>
|
||||
ipcRenderer.invoke('storage:setRelayGroups', relayGroups)
|
||||
getItem: (key: string) => ipcRenderer.invoke('storage:getItem', key),
|
||||
setItem: (key: string, value: string) => ipcRenderer.invoke('storage:setItem', key, value)
|
||||
},
|
||||
nostr: {
|
||||
login: (nsec: string) => ipcRenderer.invoke('nostr:login', nsec),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { TTheme, TThemeSetting } from '@common/types'
|
||||
import { isElectron } from '@renderer/lib/env'
|
||||
import storage from '@renderer/services/storage.service'
|
||||
import { createContext, useContext, useEffect, useState } from 'react'
|
||||
|
||||
type ThemeProviderProps = {
|
||||
@@ -12,8 +13,10 @@ type ThemeProviderState = {
|
||||
setThemeSetting: (themeSetting: TThemeSetting) => Promise<void>
|
||||
}
|
||||
|
||||
// web only
|
||||
function getSystemTheme() {
|
||||
async function getSystemTheme() {
|
||||
if (isElectron(window)) {
|
||||
return await window.api.theme.current()
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
@@ -27,33 +30,28 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
// electron
|
||||
if (isElectron(window)) {
|
||||
const [themeSetting, theme] = await Promise.all([
|
||||
window.api.theme.themeSetting(),
|
||||
window.api.theme.current()
|
||||
])
|
||||
setTheme(theme)
|
||||
setThemeSetting(themeSetting)
|
||||
|
||||
window.api.theme.onChange((theme) => {
|
||||
setTheme(theme)
|
||||
})
|
||||
} else {
|
||||
// web
|
||||
if (themeSetting === 'system') {
|
||||
setTheme(getSystemTheme())
|
||||
return
|
||||
}
|
||||
setTheme(themeSetting)
|
||||
const themeSetting = await storage.getThemeSetting()
|
||||
if (themeSetting === 'system') {
|
||||
setTheme(await getSystemTheme())
|
||||
return
|
||||
}
|
||||
setTheme(themeSetting)
|
||||
}
|
||||
|
||||
init()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (themeSetting !== 'system' || isElectron(window)) return
|
||||
if (themeSetting !== 'system') return
|
||||
|
||||
if (isElectron(window)) {
|
||||
window.api.theme.addChangeListener((theme) => {
|
||||
setTheme(theme)
|
||||
})
|
||||
return () => {
|
||||
isElectron(window) && window.api.theme.removeChangeListener()
|
||||
}
|
||||
}
|
||||
|
||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||
const handleChange = (e: MediaQueryListEvent) => {
|
||||
@@ -80,14 +78,10 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
|
||||
const value = {
|
||||
themeSetting: themeSetting,
|
||||
setThemeSetting: async (themeSetting: TThemeSetting) => {
|
||||
if (isElectron(window)) {
|
||||
await window.api.theme.set(themeSetting)
|
||||
} else {
|
||||
localStorage.setItem('themeSetting', themeSetting)
|
||||
}
|
||||
await storage.setThemeSetting(themeSetting)
|
||||
setThemeSetting(themeSetting)
|
||||
if (themeSetting === 'system') {
|
||||
setTheme(getSystemTheme())
|
||||
setTheme(await getSystemTheme())
|
||||
return
|
||||
}
|
||||
setTheme(themeSetting)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { TRelayGroup } from '@common/types'
|
||||
import { StorageKey } from '@common/constants'
|
||||
import { TRelayGroup, TThemeSetting } from '@common/types'
|
||||
import { isElectron } from '@renderer/lib/env'
|
||||
|
||||
const DEFAULT_RELAY_GROUPS: TRelayGroup[] = [
|
||||
@@ -15,21 +16,19 @@ const DEFAULT_RELAY_GROUPS: TRelayGroup[] = [
|
||||
]
|
||||
|
||||
class Storage {
|
||||
async getRelayGroups() {
|
||||
async getItem(key: string) {
|
||||
if (isElectron(window)) {
|
||||
const relayGroups = await window.api.storage.getRelayGroups()
|
||||
return relayGroups ?? DEFAULT_RELAY_GROUPS
|
||||
return window.api.storage.getItem(key)
|
||||
} else {
|
||||
const relayGroupsStr = localStorage.getItem('relayGroups')
|
||||
return relayGroupsStr ? (JSON.parse(relayGroupsStr) as TRelayGroup[]) : DEFAULT_RELAY_GROUPS
|
||||
return localStorage.getItem(key)
|
||||
}
|
||||
}
|
||||
|
||||
async setRelayGroups(relayGroups: TRelayGroup[]) {
|
||||
async setItem(key: string, value: string) {
|
||||
if (isElectron(window)) {
|
||||
return window.api.storage.setRelayGroups(relayGroups)
|
||||
return window.api.storage.setItem(key, value)
|
||||
} else {
|
||||
localStorage.setItem('relayGroups', JSON.stringify(relayGroups))
|
||||
return localStorage.setItem(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +38,7 @@ class StorageService {
|
||||
|
||||
private initPromise!: Promise<void>
|
||||
private relayGroups: TRelayGroup[] = []
|
||||
private themeSetting: TThemeSetting = 'system'
|
||||
private storage: Storage = new Storage()
|
||||
|
||||
constructor() {
|
||||
@@ -50,7 +50,10 @@ class StorageService {
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.relayGroups = await this.storage.getRelayGroups()
|
||||
const relayGroupsStr = await this.storage.getItem(StorageKey.RELAY_GROUPS)
|
||||
this.relayGroups = relayGroupsStr ? JSON.parse(relayGroupsStr) : DEFAULT_RELAY_GROUPS
|
||||
this.themeSetting =
|
||||
((await this.storage.getItem(StorageKey.THEME_SETTING)) as TThemeSetting) ?? 'system'
|
||||
}
|
||||
|
||||
async getRelayGroups() {
|
||||
@@ -60,9 +63,20 @@ class StorageService {
|
||||
|
||||
async setRelayGroups(relayGroups: TRelayGroup[]) {
|
||||
await this.initPromise
|
||||
await this.storage.setRelayGroups(relayGroups)
|
||||
await this.storage.setItem(StorageKey.RELAY_GROUPS, JSON.stringify(relayGroups))
|
||||
this.relayGroups = relayGroups
|
||||
}
|
||||
|
||||
async getThemeSetting() {
|
||||
await this.initPromise
|
||||
return this.themeSetting
|
||||
}
|
||||
|
||||
async setThemeSetting(themeSetting: TThemeSetting) {
|
||||
await this.initPromise
|
||||
await this.storage.setItem(StorageKey.THEME_SETTING, themeSetting)
|
||||
this.themeSetting = themeSetting
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new StorageService()
|
||||
|
||||
Reference in New Issue
Block a user