summaryrefslogtreecommitdiff
path: root/client-web/source
diff options
context:
space:
mode:
Diffstat (limited to 'client-web/source')
-rw-r--r--client-web/source/helper.ts3
-rw-r--r--client-web/source/preferences/decl.ts4
-rw-r--r--client-web/source/preferences/mod.ts13
-rw-r--r--client-web/source/preferences/ui.ts79
4 files changed, 87 insertions, 12 deletions
diff --git a/client-web/source/helper.ts b/client-web/source/helper.ts
index 60ecf09..a319172 100644
--- a/client-web/source/helper.ts
+++ b/client-web/source/helper.ts
@@ -36,6 +36,9 @@ export const eh4 = elem_with_content("h4")
export const eh5 = elem_with_content("h5")
export const eh6 = elem_with_content("h6")
export const ediv = elem_with_children("div")
+export const etr = elem_with_children("tr")
+export const etd = elem_with_children("td")
+export const eth = elem_with_children("th")
export const espan = elem_with_content("span")
export const elabel = elem_with_content("label")
export const ebutton = elem_with_content("button")
diff --git a/client-web/source/preferences/decl.ts b/client-web/source/preferences/decl.ts
index 6590eea..0d82bd2 100644
--- a/client-web/source/preferences/decl.ts
+++ b/client-web/source/preferences/decl.ts
@@ -7,6 +7,8 @@ export function hex_id(len = 8): string {
// TODO this could be simpler
const string = "", bool = false, number = 0; // example types for ts
+const optional = <T>(a: T): T | undefined => a
+
export const PREF_DECLS = {
username: { type: string, default: "guest-" + hex_id(), description: "Username" },
warn_redirect: { type: bool, default: false, description: "Interal option that is set by a server redirect." },
@@ -21,7 +23,7 @@ export const PREF_DECLS = {
microphone_gain: { type: number, default: 1, description: "Amplify microphone volume" },
video_fps: { type: number, description: "Preferred framerate (in 1/s) for screencast and camera" },
video_resolution: { type: number, description: "Preferred width for screencast and camera" },
- camera_facing_mode: { type: string, possible_values: ["environment", "user"], description: "Prefer user-facing or env-facing camera" },
+ camera_facing_mode: { type: optional(string), possible_values: ["environment", "user"], description: "Prefer user-facing or env-facing camera" },
auto_gain_control: { type: bool, description: "Automatically adjust mic gain" },
echo_cancellation: { type: bool, description: "Cancel echo" },
diff --git a/client-web/source/preferences/mod.ts b/client-web/source/preferences/mod.ts
index 5b33924..5f238bb 100644
--- a/client-web/source/preferences/mod.ts
+++ b/client-web/source/preferences/mod.ts
@@ -1,3 +1,4 @@
+import { log } from "../logger.ts";
import { PREF_DECLS } from "./decl.ts";
@@ -15,7 +16,15 @@ type TypeMapper = { "string": string, "number": number, "boolean": boolean }
type PrefMap<T extends { [key: string]: { type: unknown } }> = { [Key in keyof T]: T[Key]["type"] }
type Optional<T extends { [key: string]: unknown }> = { [Key in keyof T]?: T[Key] }
export const { prefs: PREFS, explicit: PREFS_EXPLICIT } = register_prefs(PREF_DECLS)
-export const on_pref_change_handlers: ((key: keyof typeof PREFS) => void)[] = []
+const pref_change_handlers: Map<keyof typeof PREFS, Set<() => unknown>> = new Map()
+export const on_pref_changed = (key: keyof typeof PREFS, cb: () => unknown) =>
+ (pref_change_handlers.get(key)
+ ?? (() => {
+ const n = new Set<() => unknown>();
+ pref_change_handlers.set(key, n);
+ return n
+ })()
+ ).add(cb)
export function register_prefs<T extends Record<string, PrefDecl<unknown>>>(ds: T): { prefs: PrefMap<T>, explicit: Optional<PrefMap<T>> } {
const prefs: PrefMap<T> = {} as PrefMap<T>
@@ -33,10 +42,12 @@ export function register_prefs<T extends Record<string, PrefDecl<unknown>>>(ds:
}
export function change_pref<T extends keyof typeof PREFS>(key: T, value: typeof PREFS[T]) {
+ log("*", `pref changed: ${key}`)
PREFS[key] = value
if ((PREF_DECLS as Record<string, PrefDecl<unknown>>)[key].default != value)
PREFS_EXPLICIT[key] = value
else delete PREFS_EXPLICIT[key]
+ pref_change_handlers.get(key)?.forEach(h => h())
window.location.hash = "#" + generate_section()
}
export function generate_section(): string {
diff --git a/client-web/source/preferences/ui.ts b/client-web/source/preferences/ui.ts
index 3461da9..4b0832a 100644
--- a/client-web/source/preferences/ui.ts
+++ b/client-web/source/preferences/ui.ts
@@ -1,14 +1,30 @@
-import { ebr, ebutton, ediv, elabel, espan, OverlayUi } from "../helper.ts";
+import { ebr, ebutton, ediv, elabel, espan, etd, etr, OverlayUi } from "../helper.ts";
import { PREF_DECLS } from "./decl.ts";
-import { change_pref, PrefDecl, PREFS } from "./mod.ts";
+import { change_pref, on_pref_changed, PrefDecl, PREFS } from "./mod.ts";
export class PrefUi extends OverlayUi {
constructor() {
- const elements = Object.entries(PREF_DECLS as Record<string, PrefDecl<unknown>>).map(([key_, decl]) => {
- const key = key_ as keyof typeof PREF_DECLS
+ console.log(PREFS);
- if (typeof decl.type == "boolean") {
- const id = `pref-check-${key}`
+ const rows = Object.entries(PREF_DECLS as Record<string, PrefDecl<unknown>>).map(([key_, decl]) => {
+ const key = key_ as keyof typeof PREF_DECLS
+ const id = `pref-${key}`
+ let prim_control: HTMLInputElement | HTMLSelectElement | undefined;
+ if (decl.possible_values) {
+ const sel = document.createElement("select")
+ sel.id = id
+ sel.value = JSON.stringify(PREFS[key])
+ for (const v of decl.possible_values) {
+ const opt = document.createElement("option")
+ opt.value = opt.textContent = JSON.stringify(v ?? null)
+ sel.append(opt)
+ }
+ sel.onchange = () => {
+ change_pref(key, JSON.parse(sel.value) ?? undefined)
+ }
+ on_pref_changed(key, () => sel.value = JSON.stringify(PREFS[key] ?? null))
+ prim_control = sel
+ } else if (typeof decl.type == "boolean") {
const checkbox = document.createElement("input")
checkbox.type = "checkbox"
checkbox.id = id
@@ -16,16 +32,59 @@ export class PrefUi extends OverlayUi {
checkbox.onchange = () => {
change_pref(key, checkbox.checked)
}
- const label = elabel(decl.description ?? `[${key}]`, { id })
- return ediv({ class: "pref" }, checkbox, label)
+ on_pref_changed(key, () => checkbox.checked = PREFS[key] as boolean)
+ prim_control = checkbox
+ } else if (typeof decl.type == "string") {
+ const textbox = document.createElement("input")
+ textbox.type = "text"
+ textbox.id = id
+ textbox.value = PREFS[key] as string
+ textbox.onchange = () => {
+ change_pref(key, textbox.value)
+ }
+ on_pref_changed(key, () => textbox.value = PREFS[key] as string)
+ prim_control = textbox
+ } else if (typeof decl.type == "number") {
+ const textbox = document.createElement("input")
+ textbox.type = "number"
+ textbox.id = id
+ textbox.value = PREFS[key] as string
+ textbox.onchange = () => {
+ change_pref(key, parseFloat(textbox.value))
+ }
+ on_pref_changed(key, () => textbox.value = PREFS[key] as string)
+ prim_control = textbox
}
- return espan(`(not implemented)`)
+
+ let use_opt_;
+ if (decl.default === undefined || decl.optional) {
+ const use_opt = document.createElement("input")
+ use_opt.type = "checkbox"
+ use_opt.id = id
+ use_opt.checked = PREFS[key] !== undefined
+ if (prim_control) prim_control.disabled = !use_opt.checked
+ use_opt.onchange = () => {
+ if (use_opt.checked) { if (prim_control?.onchange) prim_control.onchange(new Event("change")) }
+ else change_pref(key, undefined)
+ }
+ on_pref_changed(key, () => {
+ use_opt.checked = PREFS[key] !== undefined
+ if (prim_control) prim_control.disabled = !use_opt.checked
+ })
+ use_opt_ = use_opt;
+ }
+
+ const label = elabel(decl.description ?? `[${key}]`, { id })
+ return etr({ class: "pref" }, etd({}, label), etd({}, use_opt_ ?? ""), etd({}, prim_control ?? ""))
})
const notification_perm = Notification.permission == "granted" ? ediv() : ediv({},
espan("For keks-meet to send notifications, it needs you to grant permission: "),
ebutton("Grant", { onclick: () => Notification.requestPermission() }),
)
- super(ediv({ class: "prefs-overlay" }, notification_perm, ebr(), ...elements))
+ const table = document.createElement("table")
+ table.append(...rows)
+
+ super(ediv({ class: "prefs-overlay" }, notification_perm, ebr(), table))
}
}