feat(client): add storage service and persist display settings

This commit is contained in:
neon
2025-12-22 18:08:28 -06:00
parent 7722d7b87b
commit 28b00e8ff3
5 changed files with 45 additions and 13 deletions
@@ -0,0 +1,14 @@
import { ref, watch } from 'vue'
import { storage } from '@/services/storage'
export function useDisplaySettings() {
const coverSize = ref(storage.get('coverSize', 150))
const gridGap = ref(storage.get('gridGap', 16))
const viewMode = ref<'grid' | 'list'>(storage.get('viewMode', 'grid'))
watch(coverSize, (v) => storage.set('coverSize', v))
watch(gridGap, (v) => storage.set('gridGap', v))
watch(viewMode, (v) => storage.set('viewMode', v))
return { coverSize, gridGap, viewMode }
}
+19
View File
@@ -0,0 +1,19 @@
function get<T>(key: string, fallback: T): T {
const raw = localStorage.getItem(key)
if (raw === null) return fallback
try {
return JSON.parse(raw) as T
} catch {
return fallback
}
}
function set<T>(key: string, value: T): void {
localStorage.setItem(key, JSON.stringify(value))
}
function remove(key: string): void {
localStorage.removeItem(key)
}
export const storage = { get, set, remove }
+9 -9
View File
@@ -1,5 +1,6 @@
import { defineStore } from 'pinia'
import { ref, watch } from 'vue'
import { storage } from '@/services/storage'
type Theme = 'light' | 'dark'
export type Accent = 'neutral' | 'violet' | 'blue' | 'cyan' | 'green' | 'amber' | 'orange' | 'rose'
@@ -26,15 +27,14 @@ const ACCENT_IDS = ACCENT_OPTIONS.map((a) => a.id)
const RADIUS_IDS = RADIUS_OPTIONS.map((r) => r.id)
export const useThemeStore = defineStore('theme', () => {
const storedTheme = localStorage.getItem('theme') as Theme | null
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
const theme = ref<Theme>(storedTheme ?? (prefersDark ? 'dark' : 'light'))
const theme = ref<Theme>(storage.get<Theme>('theme', prefersDark ? 'dark' : 'light'))
const storedAccent = localStorage.getItem('accent') as Accent | null
const accent = ref<Accent>(storedAccent && ACCENT_IDS.includes(storedAccent) ? storedAccent : 'neutral')
const storedAccent = storage.get<Accent>('accent', 'neutral')
const accent = ref<Accent>(ACCENT_IDS.includes(storedAccent) ? storedAccent : 'neutral')
const storedRadius = localStorage.getItem('radius') as Radius | null
const radius = ref<Radius>(storedRadius && RADIUS_IDS.includes(storedRadius) ? storedRadius : 'default')
const storedRadius = storage.get<Radius>('radius', 'default')
const radius = ref<Radius>(RADIUS_IDS.includes(storedRadius) ? storedRadius : 'default')
function applyTheme(t: Theme) {
document.documentElement.classList.toggle('dark', t === 'dark')
@@ -66,7 +66,7 @@ export const useThemeStore = defineStore('theme', () => {
theme,
(t) => {
applyTheme(t)
localStorage.setItem('theme', t)
storage.set('theme', t)
},
{ immediate: true },
)
@@ -74,7 +74,7 @@ export const useThemeStore = defineStore('theme', () => {
accent,
(a) => {
applyAccent(a)
localStorage.setItem('accent', a)
storage.set('accent', a)
},
{ immediate: true },
)
@@ -82,7 +82,7 @@ export const useThemeStore = defineStore('theme', () => {
radius,
(r) => {
applyRadius(r)
localStorage.setItem('radius', r)
storage.set('radius', r)
},
{ immediate: true },
)
+2 -3
View File
@@ -5,10 +5,9 @@ import AppHeader from '@/components/AppHeader.vue'
import AppSidebar from '@/components/AppSidebar.vue'
import { SidebarProvider, SidebarInset } from '@/components/ui/sidebar'
import { useBooks } from '@/features/book/composables/useBooks'
import { useDisplaySettings } from '@/composables/useDisplaySettings'
const coverSize = ref(150)
const gridGap = ref(16)
const viewMode = ref<'grid' | 'list'>('grid')
const { coverSize, gridGap, viewMode } = useDisplaySettings()
const libraryId = ref<number | null>(null)
async function loadLibrary() {