diff --git a/web/src/api/types/preferences/localstorage.ts b/web/src/api/types/preferences/localstorage.ts
index c74c984..c4992a0 100644
--- a/web/src/api/types/preferences/localstorage.ts
+++ b/web/src/api/types/preferences/localstorage.ts
@@ -13,7 +13,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-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
},
})
}
diff --git a/web/src/api/types/preferences/preferences.ts b/web/src/api/types/preferences/preferences.ts
index 6831a58..1404667 100644
--- a/web/src/api/types/preferences/preferences.ts
+++ b/web/src/api/types/preferences/preferences.ts
@@ -13,6 +13,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
+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({
- 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({
+ 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)
+}
diff --git a/web/src/api/types/preferences/proxy.ts b/web/src/api/types/preferences/proxy.ts
index 97818bd..d9cd11a 100644
--- a/web/src/api/types/preferences/proxy.ts
+++ b/web/src/api/types/preferences/proxy.ts
@@ -14,18 +14,23 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
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
},
})
}