fix: persist font selections across navigation and page reloads

- Use v-model with writable computeds instead of :value+@change so selects
  re-sync when the options list changes asynchronously
- Always include currently chosen fonts in sortedFontFamilies so a selected
  font not present in .remanso.json fontFamilies still shows in the select
- Initialize userSettings instead of returning early in font setters so
  changes made before async GitHub fetch completes are not silently dropped
- Back font choices with localStorage so they survive hard reloads even when
  PouchDB/IndexedDB fails silently in the web worker
This commit is contained in:
Julien Calixte
2026-04-06 18:51:27 +02:00
parent c197b80095
commit 73a6014750
2 changed files with 60 additions and 14 deletions

View File

@@ -22,10 +22,28 @@ const DEFAULT_FONT_FAMILIES = [
const fontFamilies = computed( const fontFamilies = computed(
() => store.userSettings?.fontFamilies ?? DEFAULT_FONT_FAMILIES () => store.userSettings?.fontFamilies ?? DEFAULT_FONT_FAMILIES
) )
const sortedFontFamilies = computed(() => const sortedFontFamilies = computed(() => {
[...fontFamilies.value].sort((a, b) => a.localeCompare(b)) const base = fontFamilies.value
) const extras = [
store.userSettings?.chosenTitleFont,
store.userSettings?.chosenBodyFont
].filter((f): f is string => !!f && !base.includes(f))
return [...base, ...extras].sort((a, b) => a.localeCompare(b))
})
const fontSizes = Array.from({ length: 7 }, (_, i) => `${9 + i * 2}pt`) const fontSizes = Array.from({ length: 7 }, (_, i) => `${9 + i * 2}pt`)
const titleFont = computed({
get: () => store.userSettings?.chosenTitleFont,
set: (value) => store.setTitleFont(value!)
})
const bodyFont = computed({
get: () => store.userSettings?.chosenBodyFont,
set: (value) => store.setBodyFont(value!)
})
const fontSize = computed({
get: () => store.userSettings?.chosenFontSize,
set: (value) => store.setFontSize(value!)
})
</script> </script>
<template> <template>
@@ -35,8 +53,7 @@ const fontSizes = Array.from({ length: 7 }, (_, i) => `${9 + i * 2}pt`)
<select <select
id="title-font" id="title-font"
class="select" class="select"
:value="store.userSettings?.chosenTitleFont" v-model="titleFont"
@change="store.setTitleFont(($event.target as HTMLSelectElement).value)"
> >
<option v-for="font in sortedFontFamilies" :key="font" :value="font"> <option v-for="font in sortedFontFamilies" :key="font" :value="font">
{{ font }} {{ font }}
@@ -47,8 +64,7 @@ const fontSizes = Array.from({ length: 7 }, (_, i) => `${9 + i * 2}pt`)
<select <select
id="body-font" id="body-font"
class="select" class="select"
:value="store.userSettings?.chosenBodyFont" v-model="bodyFont"
@change="store.setBodyFont(($event.target as HTMLSelectElement).value)"
> >
<option v-for="font in sortedFontFamilies" :key="font" :value="font"> <option v-for="font in sortedFontFamilies" :key="font" :value="font">
{{ font }} {{ font }}
@@ -62,8 +78,7 @@ const fontSizes = Array.from({ length: 7 }, (_, i) => `${9 + i * 2}pt`)
<select <select
id="font-size" id="font-size"
class="select" class="select"
:value="store.userSettings?.chosenFontSize" v-model="fontSize"
@change="store.setFontSize(($event.target as HTMLSelectElement).value)"
> >
<option v-for="size in fontSizes" :key="size" :value="size"> <option v-for="size in fontSizes" :key="size" :value="size">
{{ size }} {{ size }}

View File

@@ -34,11 +34,37 @@ export const useUserRepoStore = defineStore("USER_REPO_STATE", {
_requestId: 0 _requestId: 0
}), }),
actions: { actions: {
_persistFonts() {
if (!this.userSettings) return
try {
const { chosenTitleFont, chosenBodyFont, chosenFontSize, chosenFontFamily } =
this.userSettings
localStorage.setItem(
`remanso:fonts:${this.user}:${this.repo}`,
JSON.stringify({ chosenTitleFont, chosenBodyFont, chosenFontSize, chosenFontFamily })
)
} catch {
// ignore
}
},
async setUserRepo(user: string, repo: string) { async setUserRepo(user: string, repo: string) {
const requestId = ++this._requestId const requestId = ++this._requestId
this.user = user this.user = user
this.repo = repo this.repo = repo
let lsFonts: Partial<UserSettings> = {}
try {
const lsRaw = localStorage.getItem(`remanso:fonts:${user}:${repo}`)
if (lsRaw) lsFonts = JSON.parse(lsRaw)
} catch {
// ignore
}
if (Object.keys(lsFonts).length) {
if (!this.userSettings) this.userSettings = { $type: DataType.UserSettings }
Object.assign(this.userSettings, lsFonts)
}
const savedRepoId = generateId(DataType.SavedRepo, `${user}-${repo}`) const savedRepoId = generateId(DataType.SavedRepo, `${user}-${repo}`)
const userSettingsId = `UserSetting-${user}-${repo}` const userSettingsId = `UserSetting-${user}-${repo}`
@@ -54,7 +80,8 @@ export const useUserRepoStore = defineStore("USER_REPO_STATE", {
} }
if (cachedUserSettings) { if (cachedUserSettings) {
this.userSettings = cachedUserSettings // localStorage font choices take priority over PouchDB cache
this.userSettings = { ...cachedUserSettings, ...lsFonts }
} }
try { try {
@@ -157,10 +184,11 @@ export const useUserRepoStore = defineStore("USER_REPO_STATE", {
}, },
setFontFamily(fontFamily: string) { setFontFamily(fontFamily: string) {
if (!this.userSettings) { if (!this.userSettings) {
return this.userSettings = { $type: DataType.UserSettings }
} }
this.userSettings.chosenFontFamily = fontFamily this.userSettings.chosenFontFamily = fontFamily
this._persistFonts()
const userSettingsId = `UserSetting-${this.user}-${this.repo}` const userSettingsId = `UserSetting-${this.user}-${this.repo}`
data.update<DataType.UserSettings, UserSettings>({ data.update<DataType.UserSettings, UserSettings>({
...this.userSettings, ...this.userSettings,
@@ -169,10 +197,11 @@ export const useUserRepoStore = defineStore("USER_REPO_STATE", {
}, },
setFontSize(fontSize: string) { setFontSize(fontSize: string) {
if (!this.userSettings) { if (!this.userSettings) {
return this.userSettings = { $type: DataType.UserSettings }
} }
this.userSettings.chosenFontSize = fontSize this.userSettings.chosenFontSize = fontSize
this._persistFonts()
const userSettingsId = `UserSetting-${this.user}-${this.repo}` const userSettingsId = `UserSetting-${this.user}-${this.repo}`
data.update<DataType.UserSettings, UserSettings>({ data.update<DataType.UserSettings, UserSettings>({
...this.userSettings, ...this.userSettings,
@@ -181,10 +210,11 @@ export const useUserRepoStore = defineStore("USER_REPO_STATE", {
}, },
setTitleFont(font: string) { setTitleFont(font: string) {
if (!this.userSettings) { if (!this.userSettings) {
return this.userSettings = { $type: DataType.UserSettings }
} }
this.userSettings.chosenTitleFont = font this.userSettings.chosenTitleFont = font
this._persistFonts()
const userSettingsId = `UserSetting-${this.user}-${this.repo}` const userSettingsId = `UserSetting-${this.user}-${this.repo}`
data.update<DataType.UserSettings, UserSettings>({ data.update<DataType.UserSettings, UserSettings>({
...this.userSettings, ...this.userSettings,
@@ -193,10 +223,11 @@ export const useUserRepoStore = defineStore("USER_REPO_STATE", {
}, },
setBodyFont(font: string) { setBodyFont(font: string) {
if (!this.userSettings) { if (!this.userSettings) {
return this.userSettings = { $type: DataType.UserSettings }
} }
this.userSettings.chosenBodyFont = font this.userSettings.chosenBodyFont = font
this._persistFonts()
const userSettingsId = `UserSetting-${this.user}-${this.repo}` const userSettingsId = `UserSetting-${this.user}-${this.repo}`
data.update<DataType.UserSettings, UserSettings>({ data.update<DataType.UserSettings, UserSettings>({
...this.userSettings, ...this.userSettings,