mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-17 06:30:50 +01:00
This update migrates desktop grid settings from localStorage to the database and introduces a comprehensive device management system. Features: - New haex_devices table for device identification and naming - Device-specific settings with foreign key relationships - Preset-based icon sizes (Small, Medium, Large, Extra Large) - Grid positioning improvements to prevent dragging behind PageHeader - Dynamic icon sizing based on database settings Database Changes: - Created haex_devices table with deviceId (UUID) and name columns - Modified haex_settings to include device_id FK and updated unique constraint - Migration 0002_loose_quasimodo.sql for schema changes Technical Improvements: - Replaced arbitrary icon size slider (60-200px) with preset system - Icons use actual measured dimensions for proper grid centering - Settings sync on vault mount for cross-device consistency - Proper bounds checking during icon drag operations Bump version to 0.1.7
249 lines
7.3 KiB
TypeScript
249 lines
7.3 KiB
TypeScript
import { and, eq } from 'drizzle-orm'
|
|
import { z } from 'zod'
|
|
import * as schema from '~/database/schemas/haex'
|
|
import type { Locale } from 'vue-i18n'
|
|
|
|
export enum VaultSettingsTypeEnum {
|
|
settings = 'settings',
|
|
system = 'system',
|
|
}
|
|
|
|
export enum VaultSettingsKeyEnum {
|
|
locale = 'locale',
|
|
theme = 'theme',
|
|
vaultName = 'vaultName',
|
|
desktopIconSize = 'desktopIconSize',
|
|
}
|
|
|
|
export enum DesktopIconSizePreset {
|
|
small = 'small',
|
|
medium = 'medium',
|
|
large = 'large',
|
|
extraLarge = 'extra-large',
|
|
}
|
|
|
|
export const iconSizePresetValues: Record<DesktopIconSizePreset, number> = {
|
|
[DesktopIconSizePreset.small]: 60,
|
|
[DesktopIconSizePreset.medium]: 80,
|
|
[DesktopIconSizePreset.large]: 120,
|
|
[DesktopIconSizePreset.extraLarge]: 160,
|
|
}
|
|
|
|
export const vaultDeviceNameSchema = z.string().min(3).max(255)
|
|
|
|
export const useVaultSettingsStore = defineStore('vaultSettingsStore', () => {
|
|
const { currentVault, currentVaultName } = storeToRefs(useVaultStore())
|
|
|
|
const {
|
|
public: { haexVault },
|
|
} = useRuntimeConfig()
|
|
|
|
const syncLocaleAsync = async () => {
|
|
try {
|
|
const app = useNuxtApp()
|
|
|
|
const currentLocaleRow =
|
|
await currentVault.value?.drizzle.query.haexSettings.findFirst({
|
|
where: eq(schema.haexSettings.key, VaultSettingsKeyEnum.locale),
|
|
})
|
|
|
|
if (currentLocaleRow?.value) {
|
|
const currentLocale = app.$i18n.availableLocales.find(
|
|
(locale) => locale === currentLocaleRow.value,
|
|
)
|
|
await app.$i18n.setLocale(currentLocale ?? app.$i18n.defaultLocale)
|
|
} else {
|
|
await currentVault.value?.drizzle.insert(schema.haexSettings).values({
|
|
id: crypto.randomUUID(),
|
|
key: VaultSettingsKeyEnum.locale,
|
|
type: VaultSettingsTypeEnum.settings,
|
|
value: app.$i18n.locale.value,
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.log('ERROR syncLocaleAsync', error)
|
|
}
|
|
}
|
|
|
|
const updateLocaleAsync = async (locale: Locale) => {
|
|
await currentVault.value?.drizzle
|
|
.update(schema.haexSettings)
|
|
.set({ key: VaultSettingsKeyEnum.locale, value: locale })
|
|
.where(
|
|
and(
|
|
eq(schema.haexSettings.key, VaultSettingsKeyEnum.locale),
|
|
eq(schema.haexSettings.type, VaultSettingsTypeEnum.settings),
|
|
),
|
|
)
|
|
}
|
|
const syncThemeAsync = async () => {
|
|
const { defaultTheme, currentTheme, currentThemeName, availableThemes } =
|
|
storeToRefs(useUiStore())
|
|
|
|
const currentThemeRow =
|
|
await currentVault.value?.drizzle.query.haexSettings.findFirst({
|
|
where: eq(schema.haexSettings.key, VaultSettingsKeyEnum.theme),
|
|
})
|
|
|
|
if (currentThemeRow?.value) {
|
|
const theme = availableThemes.value.find(
|
|
(theme) => theme.value === currentThemeRow.value,
|
|
)
|
|
currentThemeName.value = theme?.value || defaultTheme.value
|
|
} else {
|
|
await currentVault.value?.drizzle.insert(schema.haexSettings).values({
|
|
id: crypto.randomUUID(),
|
|
key: VaultSettingsKeyEnum.theme,
|
|
type: VaultSettingsTypeEnum.settings,
|
|
value: currentTheme.value?.value,
|
|
})
|
|
}
|
|
}
|
|
|
|
const updateThemeAsync = async (theme: string) => {
|
|
return await currentVault.value?.drizzle
|
|
.update(schema.haexSettings)
|
|
.set({ key: VaultSettingsKeyEnum.theme, value: theme })
|
|
.where(eq(schema.haexSettings.key, VaultSettingsKeyEnum.theme))
|
|
}
|
|
|
|
const syncVaultNameAsync = async () => {
|
|
const currentVaultNameRow =
|
|
await currentVault.value?.drizzle.query.haexSettings.findFirst({
|
|
where: eq(schema.haexSettings.key, VaultSettingsKeyEnum.vaultName),
|
|
})
|
|
|
|
if (currentVaultNameRow?.value) {
|
|
currentVaultName.value =
|
|
currentVaultNameRow.value || haexVault.defaultVaultName || 'HaexHub'
|
|
} else {
|
|
await currentVault.value?.drizzle.insert(schema.haexSettings).values({
|
|
id: crypto.randomUUID(),
|
|
key: VaultSettingsKeyEnum.vaultName,
|
|
type: VaultSettingsTypeEnum.settings,
|
|
value: currentVaultName.value,
|
|
})
|
|
}
|
|
}
|
|
|
|
const updateVaultNameAsync = async (newVaultName?: string | null) => {
|
|
return currentVault.value?.drizzle
|
|
.update(schema.haexSettings)
|
|
.set({ value: newVaultName || haexVault.defaultVaultName || 'HaexHub' })
|
|
.where(eq(schema.haexSettings.key, 'vaultName'))
|
|
}
|
|
|
|
const readDeviceNameAsync = async (deviceId?: string) => {
|
|
const { currentVault } = useVaultStore()
|
|
|
|
if (!deviceId) return undefined
|
|
|
|
const device =
|
|
await currentVault?.drizzle?.query.haexDevices.findFirst({
|
|
where: eq(schema.haexDevices.deviceId, deviceId),
|
|
})
|
|
|
|
// Workaround für Drizzle Bug: findFirst gibt manchmal Objekt mit undefined Werten zurück
|
|
// https://github.com/drizzle-team/drizzle-orm/issues/3872
|
|
// Prüfe ob das Device wirklich existiert (id muss gesetzt sein, da NOT NULL)
|
|
if (!device?.id) return undefined
|
|
|
|
return device
|
|
}
|
|
|
|
const addDeviceNameAsync = async ({
|
|
deviceId,
|
|
deviceName,
|
|
}: {
|
|
deviceId: string
|
|
deviceName: string
|
|
}) => {
|
|
const { currentVault } = useVaultStore()
|
|
|
|
const isNameOk = vaultDeviceNameSchema.safeParse(deviceName)
|
|
if (!isNameOk.success) {
|
|
console.log('deviceName not OK', isNameOk.error)
|
|
return
|
|
}
|
|
|
|
return currentVault?.drizzle?.insert(schema.haexDevices).values({
|
|
deviceId,
|
|
name: deviceName,
|
|
})
|
|
}
|
|
|
|
const updateDeviceNameAsync = async ({
|
|
deviceId,
|
|
deviceName,
|
|
}: {
|
|
deviceId: string
|
|
deviceName: string
|
|
}) => {
|
|
const { currentVault } = useVaultStore()
|
|
|
|
const isNameOk = vaultDeviceNameSchema.safeParse(deviceName)
|
|
if (!isNameOk.success) return
|
|
|
|
return currentVault?.drizzle
|
|
?.update(schema.haexDevices)
|
|
.set({
|
|
name: deviceName,
|
|
})
|
|
.where(eq(schema.haexDevices.deviceId, deviceId))
|
|
}
|
|
|
|
const syncDesktopIconSizeAsync = async (deviceInternalId: string) => {
|
|
const iconSizeRow =
|
|
await currentVault.value?.drizzle.query.haexSettings.findFirst({
|
|
where: and(
|
|
eq(schema.haexSettings.deviceId, deviceInternalId),
|
|
eq(schema.haexSettings.key, VaultSettingsKeyEnum.desktopIconSize),
|
|
eq(schema.haexSettings.type, VaultSettingsTypeEnum.system),
|
|
),
|
|
})
|
|
|
|
if (!iconSizeRow?.id) {
|
|
// Kein Eintrag vorhanden, erstelle einen mit Default (medium)
|
|
await currentVault.value?.drizzle.insert(schema.haexSettings).values({
|
|
deviceId: deviceInternalId,
|
|
key: VaultSettingsKeyEnum.desktopIconSize,
|
|
type: VaultSettingsTypeEnum.system,
|
|
value: DesktopIconSizePreset.medium,
|
|
})
|
|
return DesktopIconSizePreset.medium
|
|
}
|
|
|
|
return iconSizeRow.value as DesktopIconSizePreset
|
|
}
|
|
|
|
const updateDesktopIconSizeAsync = async (
|
|
deviceInternalId: string,
|
|
preset: DesktopIconSizePreset,
|
|
) => {
|
|
return await currentVault.value?.drizzle
|
|
.update(schema.haexSettings)
|
|
.set({ value: preset })
|
|
.where(
|
|
and(
|
|
eq(schema.haexSettings.deviceId, deviceInternalId),
|
|
eq(schema.haexSettings.key, VaultSettingsKeyEnum.desktopIconSize),
|
|
eq(schema.haexSettings.type, VaultSettingsTypeEnum.system),
|
|
),
|
|
)
|
|
}
|
|
|
|
return {
|
|
addDeviceNameAsync,
|
|
readDeviceNameAsync,
|
|
syncLocaleAsync,
|
|
syncThemeAsync,
|
|
syncVaultNameAsync,
|
|
updateDeviceNameAsync,
|
|
updateLocaleAsync,
|
|
updateThemeAsync,
|
|
updateVaultNameAsync,
|
|
syncDesktopIconSizeAsync,
|
|
updateDesktopIconSizeAsync,
|
|
}
|
|
})
|