aboutsummaryrefslogtreecommitdiff
path: root/client-web
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-09-07 20:24:21 +0200
committermetamuffin <metamuffin@disroot.org>2023-09-07 20:24:21 +0200
commitf5fa4f7d58344c2dc722d1f37c1d7a008f6ee9b3 (patch)
treec7ac2a7497670745c73f927abf264d63a5a4805d /client-web
parent2d0761b8932f11b01e241e2db3a8f08250efe878 (diff)
downloadkeks-meet-f5fa4f7d58344c2dc722d1f37c1d7a008f6ee9b3.tar
keks-meet-f5fa4f7d58344c2dc722d1f37c1d7a008f6ee9b3.tar.bz2
keks-meet-f5fa4f7d58344c2dc722d1f37c1d7a008f6ee9b3.tar.zst
new element creation helper
Diffstat (limited to 'client-web')
-rw-r--r--client-web/source/chat.ts16
-rw-r--r--client-web/source/helper.ts79
-rw-r--r--client-web/source/index.ts11
-rw-r--r--client-web/source/logger.ts4
-rw-r--r--client-web/source/menu.ts27
-rw-r--r--client-web/source/preferences/ui.ts25
-rw-r--r--client-web/source/resource/file.ts22
-rw-r--r--client-web/source/resource/track.ts16
-rw-r--r--client-web/source/room.ts6
-rw-r--r--client-web/source/room_watches.ts10
-rw-r--r--client-web/source/user/local.ts6
-rw-r--r--client-web/source/user/mod.ts10
-rw-r--r--client-web/style/side.sass2
13 files changed, 110 insertions, 124 deletions
diff --git a/client-web/source/chat.ts b/client-web/source/chat.ts
index 7721df3..bbb668f 100644
--- a/client-web/source/chat.ts
+++ b/client-web/source/chat.ts
@@ -6,7 +6,7 @@
/// <reference lib="dom" />
import { ChatMessage } from "../../common/packets.d.ts";
-import { ediv, esection, espan, image_view, notify } from "./helper.ts";
+import { e, image_view, notify } from "./helper.ts";
import { log } from "./logger.ts";
import { chat_control } from "./menu.ts";
import { PREFS } from "./preferences/mod.ts";
@@ -26,12 +26,12 @@ export class Chat {
send.type = "text"
send.placeholder = "send a message..."
- const messages = ediv({ class: "messages", aria_live: "polite" })
- const controls = ediv({ class: "controls" })
+ const messages = e("div", { class: "messages", aria_live: "polite" })
+ const controls = e("div", { class: "controls" })
controls.append(send)
messages.append(document.createElement("hr"))
- this.element = esection({ class: "chat", aria_label: "chat", role: "dialog" }, messages, controls)
+ this.element = e("section", { class: "chat", aria_label: "chat", role: "dialog" }, messages, controls)
this.messages = messages
this.controls = controls
this.send_el = send
@@ -76,13 +76,13 @@ export class Chat {
add_message(sender: User, message: ChatMessage) {
const els = []
- if (message.text) els.push(espan(message.text, { class: "text" }))
+ if (message.text) els.push(e("span", { class: "text" }, message.text))
if (message.image) els.push(image_view(message.image, { class: "image" }))
chat_control(true)
- const e = ediv({ class: "message" }, espan(sender.display_name, { class: "author" }), ": ", ...els)
- this.messages.append(e)
- e.scrollIntoView({ block: "end", behavior: "smooth", inline: "end" })
+ const el = e("div", { class: "message" }, e("span", { class: "author" }, sender.display_name), ": ", ...els)
+ this.messages.append(el)
+ el.scrollIntoView({ block: "end", behavior: "smooth", inline: "end" })
let body_str = "(empty message)"
if (message.text) body_str = message.text
diff --git a/client-web/source/helper.ts b/client-web/source/helper.ts
index b4fd72f..ecfdf95 100644
--- a/client-web/source/helper.ts
+++ b/client-web/source/helper.ts
@@ -7,14 +7,15 @@
import { PREFS } from "./preferences/mod.ts";
-const elem = <K extends keyof HTMLElementTagNameMap>(s: K): HTMLElementTagNameMap[K] => document.createElement(s)
-
-interface Opts<El> {
+interface Opts<E> {
class?: string[] | string,
- id?: string, src?: string,
+ id?: string,
+ src?: string,
for?: string,
- onclick?: (e: El) => void,
- onchange?: (e: El) => void,
+ type?: string,
+ href?: string,
+ onclick?: (e: E) => void,
+ onchange?: (e: E) => void,
role?: "dialog"
aria_label?: string
aria_live?: "polite" | "assertive"
@@ -22,70 +23,34 @@ interface Opts<El> {
aria_popup?: "menu"
}
-function apply_opts<El extends HTMLElement>(e: El, o: Opts<El> | undefined) {
- if (!o) return
+function apply_opts<E extends HTMLElement>(e: E, o: Opts<E>) {
if (o.id) e.id = o.id
if (o.onclick) e.onclick = () => o.onclick!(e)
if (o.onchange) e.onchange = () => o.onchange!(e)
- if (o.aria_label) e.ariaLabel = o.aria_label
- if (o.aria_live) e.ariaLive = o.aria_live
if (o.for) (e as unknown as HTMLLabelElement).htmlFor = o.for
- if (o.aria_modal) e.ariaModal = "true"
- if (o.aria_popup) e.ariaHasPopup = o.aria_popup
+ if (o.type && e instanceof HTMLInputElement) e.type = o.type
+ if (o.href && e instanceof HTMLAnchorElement) e.href = o.href;
if (typeof o?.class == "string") e.classList.add(o.class)
if (typeof o?.class == "object") e.classList.add(...o.class)
+ if (o.aria_modal) e.ariaModal = "true"
+ if (o.aria_popup) e.ariaHasPopup = o.aria_popup
+ if (o.aria_label) e.ariaLabel = o.aria_label
+ if (o.aria_live) e.ariaLive = o.aria_live
}
-const element = <K extends keyof HTMLElementTagNameMap>(s: K) => (opts?: Opts<HTMLElementTagNameMap[K]>) => {
- const e = elem(s)
- apply_opts(e, opts)
- return e
-}
-const elem_with_content = <K extends keyof HTMLElementTagNameMap>(s: K) => (c: string, opts?: Opts<HTMLElementTagNameMap[K]>, ...cs: (HTMLElement | string)[]) => {
- const e = element(s)(opts)
- e.textContent = c
- for (const c of cs) {
- e.append(c)
- }
- return e
-}
-const elem_with_children = <K extends keyof HTMLElementTagNameMap>(s: K) => (opts?: Opts<HTMLElementTagNameMap[K]>, ...cs: (HTMLElement | string)[]) => {
- const e = element(s)(opts)
- for (const c of cs) {
- e.append(c)
+export function e<K extends keyof HTMLElementTagNameMap>(name: K, opts: Opts<HTMLElementTagNameMap[K]>, ...children: (HTMLElement | string)[]): HTMLElementTagNameMap[K] {
+ const el = document.createElement(name)
+ apply_opts(el, opts)
+ for (const c of children) {
+ if (typeof c == "string") el.textContent += c;
+ else el.append(c)
}
- return e
+ return el
}
-export const ep = elem_with_content("p")
-export const eh1 = elem_with_content("h1")
-export const eh2 = elem_with_content("h2")
-export const eh3 = elem_with_content("h3")
-export const eh4 = elem_with_content("h4")
-export const eh5 = elem_with_content("h5")
-export const eh6 = elem_with_content("h6")
-export const epre = elem_with_content("pre")
-export const ediv = elem_with_children("div")
-export const efooter = elem_with_children("footer")
-export const esection = elem_with_children("section")
-export const enav = elem_with_children("nav")
-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")
-export const ebr = () => document.createElement("br")
-export const einput = (type: string, opts: Opts<HTMLInputElement>) => {
- const i = element("input")(opts)
- i.type = type
- return i
-}
-
-
export function image_view(url: string, opts?: Opts<HTMLElement>): HTMLElement {
const img = document.createElement("img")
- apply_opts(img, opts)
+ apply_opts(img, opts ?? {})
img.src = url
img.alt = `Image (click to open)`
img.addEventListener("click", () => {
diff --git a/client-web/source/index.ts b/client-web/source/index.ts
index 639f0c9..f5f6b2d 100644
--- a/client-web/source/index.ts
+++ b/client-web/source/index.ts
@@ -6,7 +6,7 @@
/// <reference lib="dom" />
import { init_serviceworker } from "./sw/client.ts";
-import { ediv } from "./helper.ts";
+import { e } from "./helper.ts";
import { setup_keybinds } from "./keybinds.ts";
import { log, LOGGER_CONTAINER } from "./logger.ts"
import { load_params, PREFS } from "./preferences/mod.ts";
@@ -61,8 +61,8 @@ export async function main() {
document.body.querySelectorAll(".loading").forEach(e => e.remove())
const room_secret = load_params().rsecret
- if (!globalThis.RTCPeerConnection) return log({ scope: "webrtc", error: true }, "WebRTC not supported.")
if (!globalThis.isSecureContext) log({ scope: "*", warn: true }, "This page is not in a 'Secure Context'")
+ if (!globalThis.RTCPeerConnection) return log({ scope: "webrtc", error: true }, "WebRTC not supported.")
if (!globalThis.crypto.subtle) return log({ scope: "crypto", error: true }, "SubtleCrypto not availible")
if (!globalThis.navigator.serviceWorker) log({ scope: "*", warn: true }, "Your browser does not support the Service Worker API, forced automatic updates are unavoidable.")
if (room_secret.length < 8) log({ scope: "crypto", warn: true }, "Room name is very short. e2ee is insecure!")
@@ -81,10 +81,13 @@ export async function main() {
r = new Room(conn, rtc_config)
+ conn.control_handler = (a) => r.control_handler(a)
+ conn.relay_handler = (a, b) => r.relay_handler(a, b)
+
setup_keybinds(r)
r.on_ready = () => {
- const sud = ediv({ class: "side-ui" })
- const center = ediv({ class: "main" }, r.element, info_br(), sud)
+ const sud = e("div", { class: "side-ui" })
+ const center = e("div", { class: "main" }, r.element, info_br(), sud)
document.body.append(center, control_bar(r, sud))
}
diff --git a/client-web/source/logger.ts b/client-web/source/logger.ts
index 08dba66..20138b5 100644
--- a/client-web/source/logger.ts
+++ b/client-web/source/logger.ts
@@ -5,9 +5,9 @@
*/
/// <reference lib="dom" />
-import { ediv } from "./helper.ts";
+import { e } from "./helper.ts";
-export const LOGGER_CONTAINER = ediv({ class: "logger-container" })
+export const LOGGER_CONTAINER = e("div", { class: "logger-container" })
const log_scope_color = {
"*": "#ff4a7c",
diff --git a/client-web/source/menu.ts b/client-web/source/menu.ts
index 7bb5b43..583f28b 100644
--- a/client-web/source/menu.ts
+++ b/client-web/source/menu.ts
@@ -5,12 +5,13 @@
*/
/// <reference lib="dom" />
-import { ebutton, ediv, efooter, einput, elabel, enav, ep } from "./helper.ts"
+import { e } from "./helper.ts"
import { VERSION } from "./index.ts"
import { ui_preferences } from "./preferences/ui.ts"
import { create_file_res } from "./resource/file.ts";
import { create_camera_res, create_mic_res, create_screencast_res } from "./resource/track.ts";
import { Room } from "./room.ts"
+import { ui_room_watches } from "./room_watches.ts";
export function info_br() {
const item = (name: string, cb: (() => void) | string) => {
@@ -25,8 +26,8 @@ export function info_br() {
return p
}
- return efooter({ class: "info-br" },
- ep(`keks-meet ${VERSION}`, { class: "version" }),
+ return e("footer", { class: "info-br" },
+ e("p", { class: "version" }, `keks-meet ${VERSION}`),
item("License", "https://codeberg.org/metamuffin/keks-meet/raw/branch/master/COPYING"),
item("Source code", "https://codeberg.org/metamuffin/keks-meet"),
item("Documentation", "https://codeberg.org/metamuffin/keks-meet/src/branch/master/readme.md"),
@@ -36,24 +37,26 @@ export function info_br() {
export let chat_control: (s?: boolean) => void;
export function control_bar(room: Room, side_ui_container: HTMLElement): HTMLElement {
- const leave = ebutton("Leave", { class: "leave", onclick() { window.location.href = "/" } })
+ const leave = e("button", { class: "leave", onclick() { window.location.href = "/" } }, "Leave")
const chat = side_ui(side_ui_container, room.chat.element, "Chat")
const prefs = side_ui(side_ui_container, ui_preferences(), "Settings")
+ const rwatches = side_ui(side_ui_container, ui_room_watches(), "Known Rooms")
const local_controls = [ //ediv({ class: "local-controls", aria_label: "local resources" },
- ebutton("Microphone", { onclick: () => room.local_user.await_add_resource(create_mic_res()) }),
- ebutton("Camera", { onclick: () => room.local_user.await_add_resource(create_camera_res()) }),
- ebutton("Screen", { onclick: () => room.local_user.await_add_resource(create_screencast_res()) }),
- ebutton("File", { onclick: () => room.local_user.await_add_resource(create_file_res()) }),
+ e("button", { onclick: () => room.local_user.await_add_resource(create_mic_res()) }, "Microphone"),
+ e("button", { onclick: () => room.local_user.await_add_resource(create_camera_res()) }, "Camera"),
+ e("button", { onclick: () => room.local_user.await_add_resource(create_screencast_res()) }, "Screen"),
+ e("button", { onclick: () => room.local_user.await_add_resource(create_file_res()) }, "File"),
]
chat_control = chat.set_state;
- return enav({ class: "control-bar" }, leave, chat.el, prefs.el, ...local_controls)
+ return e("nav", { class: "control-bar" }, leave, chat.el, prefs.el, rwatches.el, ...local_controls)
}
export interface SideUI { el: HTMLElement, set_state: (s?: boolean) => void }
export function side_ui(container: HTMLElement, content: HTMLElement, label: string): SideUI {
// TODO: close other side uis
- const tray = ediv({ class: "side-tray" }, content)
- const checkbox = einput("checkbox", {
+ const tray = e("div", { class: "side-tray" }, content)
+ const checkbox = e("input", {
+ type: "checkbox",
onchange: () => {
if (checkbox.checked) {
tray.classList.add("animate-in")
@@ -69,7 +72,7 @@ export function side_ui(container: HTMLElement, content: HTMLElement, label: str
}
})
return {
- el: elabel(label, { class: "side-ui-control" }, checkbox),
+ el: e("label", { class: "side-ui-control" }, label, checkbox),
set_state(s) { checkbox.checked = s ?? !checkbox.checked; if (checkbox.onchange) checkbox.onchange(undefined as unknown as Event) }
}
}
diff --git a/client-web/source/preferences/ui.ts b/client-web/source/preferences/ui.ts
index 5b8fb5f..ba23489 100644
--- a/client-web/source/preferences/ui.ts
+++ b/client-web/source/preferences/ui.ts
@@ -5,7 +5,7 @@
*/
/// <reference lib="dom" />
-import { ebr, ebutton, ediv, eh2, elabel, espan, etd, etr } from "../helper.ts";
+import { e } from "../helper.ts";
import { PREF_DECLS } from "./decl.ts";
import { change_pref, on_pref_changed, PrefDecl, PREFS } from "./mod.ts";
@@ -78,21 +78,26 @@ export function ui_preferences(): HTMLElement {
use_opt_ = use_opt;
}
- const label = elabel(decl.description ?? `[${key}]`, { for: id })
- return etr({ class: "pref" }, etd({}, label), etd({}, use_opt_ ?? ""), etd({}, prim_control ?? ""))
+ const label = e("label", { for: id }, decl.description ?? `[${key}]`)
+ return e("tr", { class: "pref" }, e("td", {}, label), e("td", {}, use_opt_ ?? ""), e("td", {}, 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() }),
+ const notification_perm = Notification.permission == "granted" ? e("div", {}) : e("div", {},
+ e("span", {}, "For keks-meet to send notifications, it needs you to grant permission: "),
+ e("button", { onclick: () => Notification.requestPermission() }, "Grant"),
)
- const reset = ediv({},
- espan("Want to clear all settings? Use this:"),
- ebutton("RESET", { onclick: () => { if (confirm("really clear all preferences?")) { localStorage.clear(); window.location.reload() } } }),
+ const reset = e("div", {},
+ e("span", {}, "Want to clear all settings? Use this:"),
+ e("button", { onclick: () => { if (confirm("really clear all preferences?")) { localStorage.clear(); window.location.reload() } } }, "RESET"),
)
const table = document.createElement("table")
table.append(...rows)
- return ediv({ class: "preferences" }, eh2("Settings"), notification_perm, ebr(), table, ebr(), reset)
+ return e("div", { class: "preferences" },
+ e("h2", {}, "Settings"),
+ notification_perm, e("br", {}),
+ table, e("br", {}),
+ reset
+ )
}
diff --git a/client-web/source/resource/file.ts b/client-web/source/resource/file.ts
index 4deb1b6..701383b 100644
--- a/client-web/source/resource/file.ts
+++ b/client-web/source/resource/file.ts
@@ -5,7 +5,7 @@
*/
/// <reference lib="dom" />
-import { display_filesize, ebutton, ediv, espan, sleep } from "../helper.ts";
+import { display_filesize, e, sleep } from "../helper.ts";
import { log } from "../logger.ts";
import { StreamDownload } from "../download_stream.ts";
import { RemoteUser } from "../user/remote.ts";
@@ -16,17 +16,17 @@ const MAX_CHUNK_SIZE = 1 << 15;
export const resource_file: ResourceHandlerDecl = {
kind: "file",
new_remote(info, user, enable) {
- const download_button = ebutton("Download", {
+ const download_button = e("button", {
onclick: self => {
enable()
self.textContent = "Downloading…"
self.disabled = true
}
- })
+ }, "Download")
return {
info,
- el: ediv({},
- espan(`File: ${JSON.stringify(info.label)} (${display_filesize(info.size!)})`),
+ el: e("div", {},
+ e("span", {}, `File: ${JSON.stringify(info.label)} (${display_filesize(info.size!)})`),
download_button,
),
on_statechange(_s) { },
@@ -114,15 +114,15 @@ export function create_file_res(): Promise<LocalResource> {
}
function file_res_inner(file: File): LocalResource {
- const transfers_el = ediv({})
+ const transfers_el = e("div", {})
const transfers_abort = new Set<() => void>()
return {
info: { kind: "file", id: Math.random().toString(), label: file.name, size: file.size },
destroy() {
transfers_abort.forEach(abort => abort())
},
- el: ediv({ class: "file" },
- espan(`Sharing file: ${JSON.stringify(file.name)}`),
+ el: e("div", { class: "file" },
+ e("span", {}, `Sharing file: ${JSON.stringify(file.name)}`),
transfers_el
),
on_request(user, create_channel) {
@@ -189,10 +189,10 @@ function file_res_inner(file: File): LocalResource {
}
function transfer_status_el(remote: RemoteUser) {
- const status = espan("…")
- const bar = ediv({ class: "progress-bar" });
+ const status = e("span", {}, "…")
+ const bar = e("div", { class: "progress-bar" });
return {
- el: ediv({ class: "transfer-status" }, status, bar),
+ el: e("div", { class: "transfer-status" }, status, bar),
set status(s: string) {
status.textContent = `${remote.display_name}: ${s}`
},
diff --git a/client-web/source/resource/track.ts b/client-web/source/resource/track.ts
index 6c461b7..aa2643a 100644
--- a/client-web/source/resource/track.ts
+++ b/client-web/source/resource/track.ts
@@ -5,7 +5,7 @@
*/
/// <reference lib="dom" />
import { ProvideInfo } from "../../../common/packets.d.ts";
-import { ebutton, ediv, elabel } from "../helper.ts";
+import { e } from "../helper.ts";
import { log } from "../logger.ts";
import { on_pref_changed, PREFS } from "../preferences/mod.ts";
import { get_rnnoise_node } from "../rnnoise.ts";
@@ -18,20 +18,20 @@ export const resource_track: ResourceHandlerDecl = {
let enable_label = `Enable ${info.track_kind}`
if (info.label) enable_label += ` "${info.label}"`
- const enable_button = ebutton(enable_label, {
+ const enable_button = e("button", {
onclick: self => {
self.disabled = true;
self.textContent = "Awaiting track…";
enable()
}
- })
+ }, enable_label)
return {
info,
- el: ediv({}, enable_button),
+ el: e("div", {}, enable_button),
on_statechange() { },
on_enable(track, disable) {
this.el.removeChild(enable_button)
- this.el.append(ebutton("Disable", {
+ this.el.append(e("button", {
onclick: (self) => {
disable()
this.el.appendChild(enable_button)
@@ -40,7 +40,7 @@ export const resource_track: ResourceHandlerDecl = {
enable_button.textContent = enable_label;
self.remove()
}
- }))
+ }), "Disable")
if (!(track instanceof TrackHandle)) return console.warn("aservuoivasretuoip");
this.el.append(create_track_display(track))
}
@@ -51,7 +51,7 @@ export const resource_track: ResourceHandlerDecl = {
export function new_local_track(info: ProvideInfo, track: TrackHandle, ...extra_controls: HTMLElement[]): LocalResource {
return {
info,
- el: ediv({},
+ el: e("div", {},
create_track_display(track),
...extra_controls
),
@@ -181,7 +181,7 @@ export async function create_mic_res() {
if (mute.checked) gain.gain.value = Number.MIN_VALUE
else gain.gain.value = PREFS.microphone_gain
}
- const mute_label = elabel("Mute", { class: "check-button" })
+ const mute_label = e("label", { class: "check-button" }, "Mute")
mute_label.prepend(mute)
return new_local_track({ id: t.id, kind: "track", track_kind: "audio", label: "Microphone" }, t, mute_label)
diff --git a/client-web/source/room.ts b/client-web/source/room.ts
index 9d0f33d..ba18162 100644
--- a/client-web/source/room.ts
+++ b/client-web/source/room.ts
@@ -11,7 +11,7 @@ import { LocalUser } from "./user/local.ts";
import { ClientboundPacket, RelayMessage } from "../../common/packets.d.ts";
import { SignalingConnection } from "./protocol/mod.ts";
import { Chat } from "./chat.ts";
-import { ediv } from "./helper.ts";
+import { e } from "./helper.ts";
export class Room {
public remote_users: Map<number, RemoteUser> = new Map()
@@ -23,9 +23,7 @@ export class Room {
public on_ready = () => { };
constructor(public signaling: SignalingConnection, public rtc_config: RTCConfiguration) {
- this.element = ediv({ class: "room", aria_label: "user list" })
- this.signaling.control_handler = (a) => this.control_handler(a)
- this.signaling.relay_handler = (a, b) => this.relay_handler(a, b)
+ this.element = e("div", { class: "room", aria_label: "user list" })
}
control_handler(packet: ClientboundPacket) {
diff --git a/client-web/source/room_watches.ts b/client-web/source/room_watches.ts
new file mode 100644
index 0000000..331022d
--- /dev/null
+++ b/client-web/source/room_watches.ts
@@ -0,0 +1,10 @@
+import { e } from "./helper.ts";
+
+export function ui_room_watches(): HTMLElement {
+ const listing = e("div", {})
+
+ return e("div", { class: "room-watches" },
+ e("h2", {}, "Known Rooms"),
+ listing
+ )
+}
diff --git a/client-web/source/user/local.ts b/client-web/source/user/local.ts
index 15c9390..a1d43bd 100644
--- a/client-web/source/user/local.ts
+++ b/client-web/source/user/local.ts
@@ -13,7 +13,7 @@ import { User } from "./mod.ts";
import { create_camera_res, create_mic_res, create_screencast_res } from "../resource/track.ts";
import { LocalResource } from "../resource/mod.ts";
import { PREFS } from "../preferences/mod.ts";
-import { ebutton } from "../helper.ts";
+import { e } from "../helper.ts";
export class LocalUser extends User {
resources: Map<string, LocalResource> = new Map()
@@ -66,14 +66,14 @@ export class LocalUser extends User {
r.el.classList.add("resource")
r.el.classList.add(`resource-${r.info.kind}`)
r.el.append(
- ebutton("Stop", {
+ e("button", {
onclick: () => {
r.destroy()
this.el.removeChild(r.el);
this.resources.delete(provide.id)
this.room.signaling.send_relay({ provide_stop: { id: provide.id } })
}
- }),
+ }, "Stop"),
)
}
}
diff --git a/client-web/source/user/mod.ts b/client-web/source/user/mod.ts
index 93042ca..67b3cd5 100644
--- a/client-web/source/user/mod.ts
+++ b/client-web/source/user/mod.ts
@@ -5,7 +5,7 @@
*/
/// <reference lib="dom" />
-import { ediv, epre, espan } from "../helper.ts";
+import { e } from "../helper.ts";
import { Room } from "../room.ts";
export class User {
@@ -14,12 +14,12 @@ export class User {
get name() { return this._name }
get display_name() { return this.name ?? "Unknown" }
- name_el = espan(this.display_name)
- stats_el = epre("")
- el = ediv({ class: "user" })
+ name_el = e("span", {}, this.display_name)
+ stats_el = e("pre", {})
+ el = e("div", { class: "user" })
constructor(public room: Room, public id: number) {
- const info_el = ediv({ class: "info" })
+ const info_el = e("div", { class: "info" })
this.name_el.textContent = this.display_name
this.name_el.classList.add("name")
info_el.append(this.name_el, this.stats_el)
diff --git a/client-web/style/side.sass b/client-web/style/side.sass
index 423746d..72c22a5 100644
--- a/client-web/style/side.sass
+++ b/client-web/style/side.sass
@@ -39,3 +39,5 @@
width: min(50em, 100vw)
.chat
width: min(30em, 100vw)
+.room-watches
+ width: min(30em, 100vw)