mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-18 13:23:33 +08:00
Merge branch 'feat/desktop-zoom-settings-integration'
This commit is contained in:
commit
188214918b
@ -144,6 +144,7 @@ describe('Settings > General tab', () => {
|
||||
skipWebFetchPreflight: true,
|
||||
desktopNotificationsEnabled: true,
|
||||
responseLanguage: '',
|
||||
uiZoom: 1,
|
||||
webSearch: { mode: 'auto', tavilyApiKey: '', braveApiKey: '' },
|
||||
h5Access: {
|
||||
enabled: false,
|
||||
@ -167,6 +168,9 @@ describe('Settings > General tab', () => {
|
||||
setResponseLanguage: vi.fn().mockImplementation(async (language: string) => {
|
||||
useSettingsStore.setState({ responseLanguage: language })
|
||||
}),
|
||||
setUiZoom: vi.fn().mockImplementation((uiZoom: number) => {
|
||||
useSettingsStore.setState({ uiZoom })
|
||||
}),
|
||||
setWebSearch: vi.fn().mockImplementation(async (webSearch) => {
|
||||
useSettingsStore.setState({ webSearch })
|
||||
}),
|
||||
@ -251,6 +255,26 @@ describe('Settings > General tab', () => {
|
||||
expect(screen.getByRole('button', { name: 'Warm Classic' })).toHaveAttribute('aria-pressed', 'false')
|
||||
})
|
||||
|
||||
it('updates the shared UI zoom setting from the General display control', async () => {
|
||||
render(<Settings />)
|
||||
|
||||
fireEvent.click(screen.getByText('General'))
|
||||
await act(async () => {
|
||||
fireEvent.change(screen.getByLabelText('UI Zoom'), {
|
||||
target: { value: '1.25', valueAsNumber: 1.25 },
|
||||
})
|
||||
})
|
||||
|
||||
expect(useSettingsStore.getState().setUiZoom).toHaveBeenCalledWith(1.25)
|
||||
expect(screen.getByText('125%')).toBeInTheDocument()
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Reset UI zoom to 100%' }))
|
||||
})
|
||||
|
||||
expect(useSettingsStore.getState().setUiZoom).toHaveBeenLastCalledWith(1)
|
||||
})
|
||||
|
||||
it('opens the Token usage tab from Settings navigation above Diagnostics', () => {
|
||||
render(<Settings />)
|
||||
|
||||
|
||||
@ -593,7 +593,7 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) {
|
||||
)}
|
||||
|
||||
{!isMobile && (
|
||||
<div className={`border-t border-[var(--color-border)] p-3 ${expanded ? '' : 'flex justify-center'}`}>
|
||||
<div className={`absolute bottom-0 left-0 right-0 border-t border-[var(--color-border)] p-3 ${expanded ? '' : 'flex justify-center'}`}>
|
||||
<NavItem
|
||||
active={activeTabId === SETTINGS_TAB_ID}
|
||||
collapsed={!expanded}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'
|
||||
import { APP_ZOOM_STORAGE_KEY } from '../lib/appZoom'
|
||||
import { useSettingsStore } from '../stores/settingsStore'
|
||||
import { useKeyboardShortcuts } from './useKeyboardShortcuts'
|
||||
|
||||
function ShortcutHost() {
|
||||
@ -22,6 +23,7 @@ describe('useKeyboardShortcuts app zoom', () => {
|
||||
document.documentElement.removeAttribute('data-app-zoom-percent')
|
||||
document.documentElement.style.removeProperty('--app-zoom')
|
||||
document.body.style.removeProperty('zoom')
|
||||
useSettingsStore.setState({ uiZoom: 1 })
|
||||
setNavigatorPlatform('Win32')
|
||||
})
|
||||
|
||||
@ -41,6 +43,7 @@ describe('useKeyboardShortcuts app zoom', () => {
|
||||
await waitFor(() => {
|
||||
expect(window.localStorage.getItem(APP_ZOOM_STORAGE_KEY)).toBe('1.1')
|
||||
})
|
||||
expect(useSettingsStore.getState().uiZoom).toBe(1.1)
|
||||
expect(document.documentElement.getAttribute('data-app-zoom-percent')).toBe('110')
|
||||
|
||||
fireEvent.keyDown(document, {
|
||||
@ -52,6 +55,7 @@ describe('useKeyboardShortcuts app zoom', () => {
|
||||
await waitFor(() => {
|
||||
expect(window.localStorage.getItem(APP_ZOOM_STORAGE_KEY)).toBe('1')
|
||||
})
|
||||
expect(useSettingsStore.getState().uiZoom).toBe(1)
|
||||
|
||||
fireEvent.keyDown(document, {
|
||||
code: 'NumpadAdd',
|
||||
|
||||
@ -4,12 +4,10 @@ import { useChatStore } from '../stores/chatStore'
|
||||
import { useTabStore } from '../stores/tabStore'
|
||||
import { useUIStore } from '../stores/uiStore'
|
||||
import {
|
||||
applyAppZoomLevel,
|
||||
getAppZoomKeyboardAction,
|
||||
initializeAppZoom,
|
||||
nextAppZoomLevel,
|
||||
readStoredAppZoomLevel,
|
||||
} from '../lib/appZoom'
|
||||
import { useSettingsStore } from '../stores/settingsStore'
|
||||
|
||||
export function useKeyboardShortcuts() {
|
||||
const setActiveSession = useSessionStore((s) => s.setActiveSession)
|
||||
@ -20,6 +18,8 @@ export function useKeyboardShortcuts() {
|
||||
const stopGeneration = useChatStore((s) => s.stopGeneration)
|
||||
const activeTabId = useTabStore((s) => s.activeTabId)
|
||||
const chatState = useChatStore((s) => activeTabId ? s.sessions[activeTabId]?.chatState ?? 'idle' : 'idle')
|
||||
const uiZoom = useSettingsStore((s) => s.uiZoom)
|
||||
const setUiZoom = useSettingsStore((s) => s.setUiZoom)
|
||||
|
||||
const activeModalRef = useRef(activeModal)
|
||||
activeModalRef.current = activeModal
|
||||
@ -27,18 +27,17 @@ export function useKeyboardShortcuts() {
|
||||
chatStateRef.current = chatState
|
||||
const activeTabIdRef = useRef(activeTabId)
|
||||
activeTabIdRef.current = activeTabId
|
||||
const appZoomLevelRef = useRef(readStoredAppZoomLevel())
|
||||
const appZoomLevelRef = useRef(uiZoom)
|
||||
appZoomLevelRef.current = uiZoom
|
||||
|
||||
useEffect(() => {
|
||||
void initializeAppZoom()
|
||||
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
const zoomAction = getAppZoomKeyboardAction(e)
|
||||
if (zoomAction) {
|
||||
e.preventDefault()
|
||||
const nextZoom = nextAppZoomLevel(appZoomLevelRef.current, zoomAction)
|
||||
appZoomLevelRef.current = nextZoom
|
||||
void applyAppZoomLevel(nextZoom)
|
||||
setUiZoom(nextZoom)
|
||||
return
|
||||
}
|
||||
|
||||
@ -80,5 +79,5 @@ export function useKeyboardShortcuts() {
|
||||
|
||||
document.addEventListener('keydown', handler)
|
||||
return () => document.removeEventListener('keydown', handler)
|
||||
}, [closeModal, setActiveSession, setActiveView, setSidebarOpen, stopGeneration])
|
||||
}, [closeModal, setActiveSession, setActiveView, setSidebarOpen, setUiZoom, stopGeneration])
|
||||
}
|
||||
|
||||
@ -801,6 +801,9 @@ export const en = {
|
||||
'settings.general.webSearchBraveFreeHint': 'Create an account to generate a Search API key with free usage for testing.',
|
||||
'settings.general.webSearchHint': 'Auto uses native Claude web search for Claude model names, then falls back to Tavily and Brave keys.',
|
||||
'settings.general.webSearchSave': 'Save',
|
||||
'settings.general.uiZoom': 'UI Zoom',
|
||||
'settings.general.uiZoomDescription': 'Adjust the size of the entire interface.',
|
||||
'settings.general.uiZoomReset': 'Reset UI zoom to 100%',
|
||||
|
||||
// ─── Empty Session ──────────────────────────────────────
|
||||
'empty.title': 'New session',
|
||||
|
||||
@ -803,6 +803,9 @@ export const zh: Record<TranslationKey, string> = {
|
||||
'settings.general.webSearchBraveFreeHint': '注册账号后可创建 Search API Key,免费额度可用于测试。',
|
||||
'settings.general.webSearchHint': '自动模式会对 Claude 模型名优先使用原生 WebSearch,失败或非 Claude 模型时再使用 Tavily/Brave。',
|
||||
'settings.general.webSearchSave': '保存',
|
||||
'settings.general.uiZoom': '界面缩放',
|
||||
'settings.general.uiZoomDescription': '调整整个界面的显示大小。',
|
||||
'settings.general.uiZoomReset': '重置界面缩放到 100%',
|
||||
|
||||
// ─── Empty Session ──────────────────────────────────────
|
||||
'empty.title': '新建会话',
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest'
|
||||
import {
|
||||
APP_ZOOM_STORAGE_KEY,
|
||||
LEGACY_UI_ZOOM_STORAGE_KEY,
|
||||
applyAppZoomLevel,
|
||||
getAppZoomKeyboardAction,
|
||||
initializeAppZoom,
|
||||
@ -39,6 +40,15 @@ describe('appZoom', () => {
|
||||
expect(window.localStorage.getItem(APP_ZOOM_STORAGE_KEY)).toBe('1.2')
|
||||
})
|
||||
|
||||
it('reads the legacy UI zoom key when the app zoom key is absent', async () => {
|
||||
window.localStorage.setItem(LEGACY_UI_ZOOM_STORAGE_KEY, '1.25')
|
||||
|
||||
await initializeAppZoom()
|
||||
|
||||
expect(document.documentElement.getAttribute('data-app-zoom-percent')).toBe('125')
|
||||
expect(window.localStorage.getItem(APP_ZOOM_STORAGE_KEY)).toBeNull()
|
||||
})
|
||||
|
||||
it('persists app zoom changes', async () => {
|
||||
await applyAppZoomLevel(1.3)
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
export const APP_ZOOM_STORAGE_KEY = 'cc-haha-app-zoom'
|
||||
export const LEGACY_UI_ZOOM_STORAGE_KEY = 'cc-haha-ui-zoom'
|
||||
export const DEFAULT_APP_ZOOM = 1
|
||||
export const MIN_APP_ZOOM = 0.5
|
||||
export const MAX_APP_ZOOM = 2
|
||||
export const APP_ZOOM_STEP = 0.1
|
||||
export const APP_ZOOM_CONTROL_STEP = 0.05
|
||||
|
||||
export type AppZoomAction = 'in' | 'out' | 'reset'
|
||||
|
||||
@ -45,7 +47,9 @@ export function isValidStoredAppZoomLevel(value: string | null): boolean {
|
||||
export function readStoredAppZoomLevel(storage: StorageLike | null = getDefaultStorage()): number {
|
||||
if (!storage) return DEFAULT_APP_ZOOM
|
||||
try {
|
||||
return normalizeAppZoomLevel(storage.getItem(APP_ZOOM_STORAGE_KEY))
|
||||
const stored = storage.getItem(APP_ZOOM_STORAGE_KEY)
|
||||
if (stored !== null) return normalizeAppZoomLevel(stored)
|
||||
return normalizeAppZoomLevel(storage.getItem(LEGACY_UI_ZOOM_STORAGE_KEY))
|
||||
} catch {
|
||||
return DEFAULT_APP_ZOOM
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { doctorApi, type DoctorReportRepairResponse } from '../api/doctor'
|
||||
import { APP_ZOOM_STORAGE_KEY } from './appZoom'
|
||||
import { APP_ZOOM_STORAGE_KEY, LEGACY_UI_ZOOM_STORAGE_KEY } from './appZoom'
|
||||
import { DESKTOP_PERSISTENCE_VERSION_KEY } from './persistenceMigrations'
|
||||
|
||||
export const SAFE_DOCTOR_STORAGE_KEYS = [
|
||||
@ -8,6 +8,7 @@ export const SAFE_DOCTOR_STORAGE_KEYS = [
|
||||
'cc-haha-theme',
|
||||
'cc-haha-locale',
|
||||
APP_ZOOM_STORAGE_KEY,
|
||||
LEGACY_UI_ZOOM_STORAGE_KEY,
|
||||
DESKTOP_PERSISTENCE_VERSION_KEY,
|
||||
] as const
|
||||
|
||||
|
||||
@ -81,6 +81,19 @@ describe('desktop persistence migrations', () => {
|
||||
expect(window.localStorage.getItem('cc-haha-app-zoom')).toBeNull()
|
||||
})
|
||||
|
||||
test('migrates the legacy UI zoom key into app zoom storage', () => {
|
||||
window.localStorage.setItem('cc-haha-ui-zoom', '1.25')
|
||||
|
||||
const report = runDesktopPersistenceMigrations()
|
||||
|
||||
expect(report.migratedKeys).toEqual(expect.arrayContaining([
|
||||
'cc-haha-app-zoom',
|
||||
'cc-haha-ui-zoom',
|
||||
]))
|
||||
expect(window.localStorage.getItem('cc-haha-app-zoom')).toBe('1.25')
|
||||
expect(window.localStorage.getItem('cc-haha-ui-zoom')).toBeNull()
|
||||
})
|
||||
|
||||
test('does not throw if schema version persistence is blocked', () => {
|
||||
const storage = {
|
||||
getItem: window.localStorage.getItem.bind(window.localStorage),
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
import { THEME_MODES } from '../types/settings'
|
||||
import { APP_ZOOM_STORAGE_KEY, isValidStoredAppZoomLevel } from './appZoom'
|
||||
import {
|
||||
APP_ZOOM_STORAGE_KEY,
|
||||
LEGACY_UI_ZOOM_STORAGE_KEY,
|
||||
isValidStoredAppZoomLevel,
|
||||
normalizeAppZoomLevel,
|
||||
} from './appZoom'
|
||||
|
||||
export const CURRENT_DESKTOP_PERSISTENCE_SCHEMA_VERSION = 1
|
||||
export const DESKTOP_PERSISTENCE_VERSION_KEY = 'cc-haha.persistence.schemaVersion'
|
||||
@ -121,6 +126,17 @@ function normalizeAppZoomKey(storage: StorageLike, report: DesktopMigrationRepor
|
||||
storage.removeItem(APP_ZOOM_STORAGE_KEY)
|
||||
report.migratedKeys.push(APP_ZOOM_STORAGE_KEY)
|
||||
}
|
||||
|
||||
const currentValue = storage.getItem(APP_ZOOM_STORAGE_KEY)
|
||||
const legacyValue = storage.getItem(LEGACY_UI_ZOOM_STORAGE_KEY)
|
||||
if (currentValue === null && legacyValue !== null && isValidStoredAppZoomLevel(legacyValue)) {
|
||||
storage.setItem(APP_ZOOM_STORAGE_KEY, String(normalizeAppZoomLevel(legacyValue)))
|
||||
report.migratedKeys.push(APP_ZOOM_STORAGE_KEY)
|
||||
}
|
||||
if (legacyValue !== null) {
|
||||
storage.removeItem(LEGACY_UI_ZOOM_STORAGE_KEY)
|
||||
report.migratedKeys.push(LEGACY_UI_ZOOM_STORAGE_KEY)
|
||||
}
|
||||
}
|
||||
|
||||
function runMigrationStep(
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import './theme/globals.css'
|
||||
import { initializeAppZoom } from './lib/appZoom'
|
||||
import { runDesktopPersistenceMigrations } from './lib/persistenceMigrations'
|
||||
|
||||
runDesktopPersistenceMigrations()
|
||||
void initializeAppZoom()
|
||||
const [{ App }, { ErrorBoundary }, { installClientDiagnosticsCapture }, { initializeTheme }] = await Promise.all([
|
||||
import('./App'),
|
||||
import('./components/ErrorBoundary'),
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useState, useEffect, useMemo, useRef, type ReactNode } from 'react'
|
||||
import QRCode from 'qrcode'
|
||||
import { Copy, Eye, EyeOff, PowerOff, QrCode, RotateCw } from 'lucide-react'
|
||||
import { useSettingsStore } from '../stores/settingsStore'
|
||||
import { useSettingsStore, UI_ZOOM_DEFAULT, UI_ZOOM_MIN, UI_ZOOM_MAX, UI_ZOOM_STEP } from '../stores/settingsStore'
|
||||
import { useProviderStore } from '../stores/providerStore'
|
||||
import { useTranslation } from '../i18n'
|
||||
import { Modal } from '../components/shared/Modal'
|
||||
@ -1385,6 +1385,8 @@ function GeneralSettings() {
|
||||
setWebSearch,
|
||||
responseLanguage,
|
||||
setResponseLanguage,
|
||||
uiZoom,
|
||||
setUiZoom,
|
||||
} = useSettingsStore()
|
||||
const t = useTranslation()
|
||||
const [webSearchDraft, setWebSearchDraft] = useState(webSearch)
|
||||
@ -1533,6 +1535,45 @@ function GeneralSettings() {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* UI Zoom */}
|
||||
<div className="mb-8">
|
||||
<div className="mb-3 flex items-end justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<h2 className="text-base font-semibold text-[var(--color-text-primary)] mb-1">{t('settings.general.uiZoom')}</h2>
|
||||
<p className="text-sm text-[var(--color-text-tertiary)]">{t('settings.general.uiZoomDescription')}</p>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 items-center gap-2">
|
||||
<span className="min-w-[48px] rounded-md bg-[var(--color-surface-container-low)] px-2 py-1 text-center text-sm font-medium text-[var(--color-text-secondary)]">
|
||||
{Math.round(uiZoom * 100)}%
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={t('settings.general.uiZoomReset')}
|
||||
title={t('settings.general.uiZoomReset')}
|
||||
onClick={() => setUiZoom(UI_ZOOM_DEFAULT)}
|
||||
className="inline-flex h-8 items-center gap-1.5 rounded-md border border-[var(--color-border)] px-2 text-xs font-medium text-[var(--color-text-secondary)] transition-colors hover:border-[var(--color-border-focus)] hover:bg-[var(--color-surface-hover)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-focus)]"
|
||||
>
|
||||
<RotateCw className="h-3.5 w-3.5" aria-hidden="true" />
|
||||
100%
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="w-9 text-right text-xs text-[var(--color-text-tertiary)]">{Math.round(UI_ZOOM_MIN * 100)}%</span>
|
||||
<input
|
||||
type="range"
|
||||
aria-label={t('settings.general.uiZoom')}
|
||||
min={UI_ZOOM_MIN}
|
||||
max={UI_ZOOM_MAX}
|
||||
step={UI_ZOOM_STEP}
|
||||
value={uiZoom}
|
||||
onChange={(e) => setUiZoom(e.currentTarget.valueAsNumber)}
|
||||
className="flex-1"
|
||||
/>
|
||||
<span className="w-9 text-xs text-[var(--color-text-tertiary)]">{Math.round(UI_ZOOM_MAX * 100)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Language selector */}
|
||||
<h2 className="text-base font-semibold text-[var(--color-text-primary)] mb-1">{t('settings.general.languageTitle')}</h2>
|
||||
<p className="text-sm text-[var(--color-text-tertiary)] mb-3">{t('settings.general.languageDescription')}</p>
|
||||
@ -1778,7 +1819,6 @@ function GeneralSettings() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -22,6 +22,47 @@ describe('settingsStore locale defaults', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('settingsStore UI zoom', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
window.localStorage.clear()
|
||||
document.documentElement.removeAttribute('data-app-zoom-percent')
|
||||
document.documentElement.style.removeProperty('--app-zoom')
|
||||
document.body.style.removeProperty('zoom')
|
||||
})
|
||||
|
||||
it('hydrates from the app zoom storage key', async () => {
|
||||
window.localStorage.setItem('cc-haha-app-zoom', '1.25')
|
||||
|
||||
const { useSettingsStore } = await import('./settingsStore')
|
||||
|
||||
expect(useSettingsStore.getState().uiZoom).toBe(1.25)
|
||||
})
|
||||
|
||||
it('applies and persists UI zoom changes through the shared app zoom controller', async () => {
|
||||
const { useSettingsStore } = await import('./settingsStore')
|
||||
|
||||
useSettingsStore.getState().setUiZoom(1.25)
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(window.localStorage.getItem('cc-haha-app-zoom')).toBe('1.25')
|
||||
})
|
||||
expect(useSettingsStore.getState().uiZoom).toBe(1.25)
|
||||
expect(document.documentElement.getAttribute('data-app-zoom-percent')).toBe('125')
|
||||
})
|
||||
|
||||
it('clamps UI zoom changes to the supported range', async () => {
|
||||
const { useSettingsStore } = await import('./settingsStore')
|
||||
|
||||
useSettingsStore.getState().setUiZoom(9)
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(window.localStorage.getItem('cc-haha-app-zoom')).toBe('2')
|
||||
})
|
||||
expect(useSettingsStore.getState().uiZoom).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('settingsStore desktop notification persistence', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules()
|
||||
|
||||
@ -5,9 +5,22 @@ import { modelsApi } from '../api/models'
|
||||
import { h5AccessApi } from '../api/h5Access'
|
||||
import { isThemeMode, type H5AccessSettings, type PermissionMode, type EffortLevel, type ModelInfo, type ThemeMode, type WebSearchSettings } from '../types/settings'
|
||||
import type { Locale } from '../i18n'
|
||||
import {
|
||||
APP_ZOOM_CONTROL_STEP,
|
||||
DEFAULT_APP_ZOOM,
|
||||
MAX_APP_ZOOM,
|
||||
MIN_APP_ZOOM,
|
||||
applyAppZoomLevel,
|
||||
normalizeAppZoomLevel,
|
||||
readStoredAppZoomLevel,
|
||||
} from '../lib/appZoom'
|
||||
import { useUIStore } from './uiStore'
|
||||
|
||||
const LOCALE_STORAGE_KEY = 'cc-haha-locale'
|
||||
export const UI_ZOOM_MIN = MIN_APP_ZOOM
|
||||
export const UI_ZOOM_MAX = MAX_APP_ZOOM
|
||||
export const UI_ZOOM_STEP = APP_ZOOM_CONTROL_STEP
|
||||
export const UI_ZOOM_DEFAULT = DEFAULT_APP_ZOOM
|
||||
let desktopNotificationsSaveQueue: Promise<void> = Promise.resolve()
|
||||
|
||||
function getStoredLocale(): Locale {
|
||||
@ -33,6 +46,7 @@ type SettingsStore = {
|
||||
h5Access: H5AccessSettings
|
||||
h5AccessError: string | null
|
||||
responseLanguage: string
|
||||
uiZoom: number
|
||||
isLoading: boolean
|
||||
error: string | null
|
||||
|
||||
@ -55,6 +69,7 @@ type SettingsStore = {
|
||||
publicBaseUrl?: string | null
|
||||
}) => Promise<void>
|
||||
setResponseLanguage: (language: string) => Promise<void>
|
||||
setUiZoom: (zoom: number) => void
|
||||
}
|
||||
|
||||
const DEFAULT_H5_ACCESS_SETTINGS: H5AccessSettings = {
|
||||
@ -79,9 +94,16 @@ export const useSettingsStore = create<SettingsStore>((set, get) => ({
|
||||
h5Access: DEFAULT_H5_ACCESS_SETTINGS,
|
||||
h5AccessError: null,
|
||||
responseLanguage: '',
|
||||
uiZoom: readStoredAppZoomLevel(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
|
||||
setUiZoom: (zoom: number) => {
|
||||
const level = normalizeAppZoomLevel(zoom)
|
||||
set({ uiZoom: level })
|
||||
void applyAppZoomLevel(level)
|
||||
},
|
||||
|
||||
fetchAll: async () => {
|
||||
set({ isLoading: true, error: null })
|
||||
try {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user