import { proxy, ref, subscribe } from 'valtio' import { UserOverridesConfig } from 'contro-max/build/types/store' import { subscribeKey } from 'valtio/utils' import { CustomCommand } from './KeybindingsCustom' import { AuthenticatedAccount } from './serversStorage' import type { BaseServerInfo } from './AddServerOrConnect' // when opening html file locally in browser, localStorage is shared between all ever opened html files, so we try to avoid conflicts const localStoragePrefix = process.env?.SINGLE_FILE_BUILD ? 'minecraft-web-client:' : '' export interface SavedProxiesData { proxies: string[] selected: string } export interface ServerHistoryEntry { ip: string version?: string numConnects: number lastConnected: number } export interface StoreServerItem extends BaseServerInfo { lastJoined?: number description?: string optionsOverride?: Record autoLogin?: Record numConnects?: number // Track number of connections } type StorageData = { customCommands: Record | undefined username: string | undefined keybindings: UserOverridesConfig | undefined options: any proxiesData: SavedProxiesData | undefined serversHistory: ServerHistoryEntry[] authenticatedAccounts: AuthenticatedAccount[] serversList: StoreServerItem[] | undefined } const oldKeysAliases: Partial> = { serversHistory: 'serverConnectionHistory', } const migrateLegacyData = () => { const proxies = localStorage.getItem('proxies') const selectedProxy = localStorage.getItem('selectedProxy') if (proxies && selectedProxy) { appStorage.proxiesData = { proxies: JSON.parse(proxies), selected: selectedProxy, } } const username = localStorage.getItem('username') if (username && !username.startsWith('"')) { appStorage.username = username } const serversHistoryLegacy = localStorage.getItem('serverConnectionHistory') if (serversHistoryLegacy) { appStorage.serversHistory = JSON.parse(serversHistoryLegacy) } localStorage.removeItem('proxies') localStorage.removeItem('selectedProxy') localStorage.removeItem('serverConnectionHistory') } const defaultStorageData: StorageData = { customCommands: undefined, username: undefined, keybindings: undefined, options: {}, proxiesData: undefined, serversHistory: [], authenticatedAccounts: [], serversList: undefined, } export const setStorageDataOnAppConfigLoad = () => { appStorage.username ??= `mcrafter${Math.floor(Math.random() * 1000)}` } export const appStorage = proxy({ ...defaultStorageData }) window.appStorage = appStorage // Restore data from localStorage for (const key of Object.keys(defaultStorageData)) { const prefixedKey = `${localStoragePrefix}${key}` const aliasedKey = oldKeysAliases[key] const storedValue = localStorage.getItem(prefixedKey) ?? (aliasedKey ? localStorage.getItem(aliasedKey) : undefined) if (storedValue) { try { const parsed = JSON.parse(storedValue) // appStorage[key] = parsed && typeof parsed === 'object' ? ref(parsed) : parsed appStorage[key] = parsed } catch (e) { console.error(`Failed to parse stored value for ${key}:`, e) } } } const saveKey = (key: keyof StorageData) => { const prefixedKey = `${localStoragePrefix}${key}` const value = appStorage[key] if (value === undefined) { localStorage.removeItem(prefixedKey) } else { localStorage.setItem(prefixedKey, JSON.stringify(value)) } } subscribe(appStorage, (ops) => { for (const op of ops) { const [type, path, value] = op const key = path[0] saveKey(key as keyof StorageData) } }) // Subscribe to changes and save to localStorage export const resetAppStorage = () => { for (const key of Object.keys(appStorage)) { appStorage[key as keyof StorageData] = defaultStorageData[key as keyof StorageData] } for (const key of Object.keys(localStorage)) { if (key.startsWith(localStoragePrefix)) { localStorage.removeItem(key) } } } migrateLegacyData()