1
0
Fork 0
forked from Mirrors/gomuks

web/preferences: improve preference proxies

This commit is contained in:
Tulir Asokan 2024-11-17 02:37:45 +02:00
parent f4be132313
commit 41074937d3
3 changed files with 49 additions and 17 deletions

View file

@ -13,7 +13,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import { Preferences, existingPreferenceKeys, preferences } from "./preferences.ts"
import { Preferences, isValidPreferenceKey, preferences } from "./preferences.ts"
import { PreferenceContext } from "./types.ts"
function getObjectFromLocalStorage(key: string): Preferences {
@ -28,11 +28,18 @@ function getObjectFromLocalStorage(key: string): Preferences {
return {}
}
const globalPrefKeys = Object.entries(preferences)
.filter(([,pref]) => pref.allowedContexts.includes(PreferenceContext.Device))
.map(([key]) => key)
const roomPrefKeys = Object.entries(preferences)
.filter(([,pref]) => pref.allowedContexts.includes(PreferenceContext.RoomDevice))
.map(([key]) => key)
export function getLocalStoragePreferences(localStorageKey: string, onChange: () => void): Preferences {
return new Proxy(getObjectFromLocalStorage(localStorageKey), {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
set(target: Preferences, key: keyof Preferences, newValue: any): boolean {
if (!existingPreferenceKeys.has(key)) {
set(target: Preferences, key: string | symbol, newValue: any): boolean {
if (!isValidPreferenceKey(key)) {
return false
}
target[key] = newValue
@ -40,8 +47,8 @@ export function getLocalStoragePreferences(localStorageKey: string, onChange: ()
onChange()
return true
},
deleteProperty(target: Preferences, key: keyof Preferences): boolean {
if (!existingPreferenceKeys.has(key)) {
deleteProperty(target: Preferences, key: string | symbol): boolean {
if (!isValidPreferenceKey(key)) {
return false
}
delete target[key]
@ -52,13 +59,15 @@ export function getLocalStoragePreferences(localStorageKey: string, onChange: ()
return true
},
ownKeys(): string[] {
console.warn("localStorage preference proxy ownKeys called")
// This is only for debugging, so the performance doesn't matter that much
return Object.entries(preferences)
.filter(([,pref]) =>
pref.allowedContexts.includes(localStorageKey === "global_prefs"
? PreferenceContext.Device : PreferenceContext.RoomDevice))
.map(([key]) => key)
return localStorageKey === "global_prefs" ? globalPrefKeys : roomPrefKeys
},
getOwnPropertyDescriptor(_target: never, key: string | symbol): PropertyDescriptor | undefined {
const keySet = localStorageKey === "global_prefs" ? globalPrefKeys : roomPrefKeys
return (typeof key === "string" && keySet.includes(key)) ? {
configurable: true,
enumerable: true,
writable: true,
} : undefined
},
})
}

View file

@ -13,6 +13,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import type { ContentURI } from "../../types"
import { Preference, anyContext } from "./types.ts"
export const codeBlockStyles = [
@ -56,7 +57,7 @@ export const preferences = {
allowedValues: codeBlockStyles,
}),
pointer_cursor: new Preference<boolean>({
displayName: "Pointer cursor",
displayName: "Use pointer cursor",
description: "Whether to use a pointer cursor for clickable elements.",
allowedContexts: anyContext,
defaultValue: false,
@ -97,6 +98,12 @@ export const preferences = {
allowedContexts: anyContext,
defaultValue: true,
}),
custom_notification_sound: new Preference<ContentURI>({
displayName: "Custom notification sound",
description: "The mxc:// URI to a custom notification sound.",
allowedContexts: anyContext,
defaultValue: "",
}),
} as const
export const existingPreferenceKeys = new Set(Object.keys(preferences))
@ -104,3 +111,7 @@ export const existingPreferenceKeys = new Set(Object.keys(preferences))
export type Preferences = {
-readonly [name in keyof typeof preferences]?: typeof preferences[name]["defaultValue"]
}
export function isValidPreferenceKey(key: unknown): key is keyof Preferences {
return typeof key === "string" && existingPreferenceKeys.has(key)
}

View file

@ -14,18 +14,23 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import type { RoomStateStore, StateStore } from "@/api/statestore"
import { Preferences, existingPreferenceKeys, preferences } from "./preferences.ts"
import { Preferences, isValidPreferenceKey, preferences } from "./preferences.ts"
import { PreferenceContext, PreferenceValueType } from "./types.ts"
const prefKeys = Object.keys(preferences)
export function getPreferenceProxy(store: StateStore, room?: RoomStateStore): Preferences {
return new Proxy({}, {
set(): boolean {
throw new Error("The preference proxy is read-only")
},
get(_target: never, key: keyof Preferences): PreferenceValueType | undefined {
get(_target: never, key: keyof Preferences | symbol): PreferenceValueType | undefined {
if (typeof key !== "string") {
return
}
const pref = preferences[key]
if (!pref) {
return undefined
return
}
let val: typeof pref.defaultValue | undefined
for (const ctx of pref.allowedContexts) {
@ -47,7 +52,14 @@ export function getPreferenceProxy(store: StateStore, room?: RoomStateStore): Pr
return pref.defaultValue
},
ownKeys(): string[] {
return Array.from(existingPreferenceKeys)
return prefKeys
},
getOwnPropertyDescriptor(_target: never, key: string | symbol): PropertyDescriptor | undefined {
return isValidPreferenceKey(key) ? {
configurable: true,
enumerable: true,
writable: false,
} : undefined
},
})
}