diff --git a/static/js/00-config.js b/static/js/00-config.js index 490552e..83dfd7d 100644 --- a/static/js/00-config.js +++ b/static/js/00-config.js @@ -1,8 +1,6 @@ // static/js/00-config.js "use strict"; - window.Glow = window.Glow || {}; - Glow.els = { chatBox: document.getElementById("chatBox"), dmBox: document.getElementById("dmBox"), @@ -93,9 +91,9 @@ Glow.els = { muteCallButton: document.getElementById("muteCallButton"), hangupCallButton: document.getElementById("hangupCallButton"), closeCallPanelButton: document.getElementById("closeCallPanelButton"), - remoteAudio: document.getElementById("remoteAudio") + remoteAudio: document.getElementById("remoteAudio"), + emojiAutocomplete: document.getElementById("emojiAutocomplete") }; - Glow.state = { currentUser: null, csrfToken: "", @@ -150,6 +148,8 @@ Glow.state = { originalTitle: document.title, dialogResolver: null, lastFocusedElement: null, + emojiSuggestions: [], + emojiActiveInput: null, presence: { roomOnlineCount: 0, channelOnlineCount: 0, @@ -170,18 +170,34 @@ Glow.state = { invitePollTimer: null } }; - Glow.MAX_MESSAGE_LENGTH = 1000; Glow.MAX_ATTACHMENT_BYTES = 5 * 1024 * 1024; Glow.DEFAULT_ACCENT_COLOR = "#f7dce8"; - +Glow.EMOJI_ALIASES = { + smile: "😄", + grin: "😁", + joy: "😂", + heart: "❤️", + thumbs_up: "👍", + fire: "🔥", + tada: "🎉", + clap: "👏", + eyes: "👀", + rocket: "🚀", + wave: "👋", + sparkles: "✨", + thinking: "🤔", + cry: "😢", + check: "✅", + bulb: "💡", + shrug: "¯\\_(ツ)_/¯" +}; Glow.normalizeAccentColor = value => { if (typeof value !== "string") return null; const color = value.trim(); if (!/^#[0-9a-fA-F]{6}$/.test(color)) return null; return color.toLowerCase(); }; - Glow.normalizeNameColor = value => { if (value == null || value === "") return null; if (typeof value !== "string") return null; @@ -189,7 +205,6 @@ Glow.normalizeNameColor = value => { if (!/^#[0-9a-fA-F]{6}$/.test(color)) return null; return color.toLowerCase(); }; - Glow.hexToRgb = hex => { const normalized = Glow.normalizeAccentColor(hex); if (!normalized) return null; @@ -199,7 +214,6 @@ Glow.hexToRgb = hex => { b: parseInt(normalized.slice(5, 7), 16) }; }; - Glow.relativeLuminance = hex => { const rgb = Glow.hexToRgb(hex); if (!rgb) return 0; @@ -215,11 +229,9 @@ Glow.relativeLuminance = hex => { 0.0722 * channel(rgb.b) ); }; - Glow.accentTextColor = hex => { return Glow.relativeLuminance(hex) >= 0.18 ? hex : "#f2f2f4"; }; - Glow.applyAccentColor = value => { const accent = Glow.normalizeAccentColor(value) || Glow.DEFAULT_ACCENT_COLOR; @@ -239,12 +251,10 @@ Glow.applyAccentColor = value => { localStorage.setItem("glow_accent_color", accent); Glow.updateThemeButton?.(); }; - Glow.resetAccentColor = () => { localStorage.removeItem("glow_accent_color"); Glow.applyAccentColor(Glow.DEFAULT_ACCENT_COLOR); }; - Glow.roleDefaultNameColor = role => { switch (String(role || "").toLowerCase()) { case "admin": @@ -255,7 +265,6 @@ Glow.roleDefaultNameColor = role => { return "#f5f5f7"; } }; - Glow.resolveUserNameColor = userish => { const explicit = Glow.normalizeNameColor(userish?.name_color) || @@ -263,22 +272,18 @@ Glow.resolveUserNameColor = userish => { if (explicit) return explicit; return Glow.roleDefaultNameColor(userish?.role || userish?.user_role); }; - Glow.applyUserColor = (element, userish) => { if (!(element instanceof HTMLElement)) return; element.style.color = Glow.resolveUserNameColor(userish); }; - Glow.canModerate = () => { const user = Glow.state.currentUser; return Boolean(user && ["moderator", "admin"].includes(user.role)); }; - Glow.isAdmin = () => { const user = Glow.state.currentUser; return Boolean(user && user.role === "admin"); }; - Glow.showToast = (message, type = "") => { clearTimeout(Glow.state.toastTimer); Glow.els.statusToast.textContent = String(message); @@ -287,17 +292,14 @@ Glow.showToast = (message, type = "") => { Glow.els.statusToast.className = "status-toast"; }, 3500); }; - Glow.showAuthNotice = (message, type = "error") => { Glow.els.authNotice.textContent = String(message); Glow.els.authNotice.className = `notice show ${type}`; }; - Glow.clearAuthNotice = () => { Glow.els.authNotice.textContent = ""; Glow.els.authNotice.className = "notice"; }; - Glow.showBlocked = message => { Glow.els.blockedMessage.textContent = message || "Access has been blocked."; Glow.els.blockedOverlay.classList.remove("hidden"); @@ -309,7 +311,6 @@ Glow.showBlocked = message => { Glow.els.attachmentButton.classList.add("disabled"); clearTimeout(Glow.state.pollTimer); }; - Glow.relativeTime = iso => { const seconds = Math.max( 0, @@ -323,7 +324,6 @@ Glow.relativeTime = iso => { if (hours < 24) return `${hours}h`; return `${Math.floor(hours / 24)}d`; }; - Glow.formatDateLabel = iso => { const date = new Date(iso); const now = new Date(); @@ -339,7 +339,6 @@ Glow.formatDateLabel = iso => { day: "numeric" }); }; - Glow.dateKey = iso => { const date = new Date(iso); return [ @@ -348,7 +347,6 @@ Glow.dateKey = iso => { String(date.getDate()).padStart(2, "0") ].join("-"); }; - Glow.isNearBottom = () => { const activeBox = Glow.state.activeView === "feed" @@ -361,41 +359,34 @@ Glow.isNearBottom = () => { if (!activeBox) return true; return activeBox.scrollHeight - activeBox.scrollTop - activeBox.clientHeight < 100; }; - Glow.autoResizeComposer = () => { const input = Glow.els.messageInput; input.style.height = "auto"; input.style.height = `${Math.min(input.scrollHeight, 150)}px`; }; - Glow.updateMessageCount = () => { const count = Glow.els.messageInput.value.length; Glow.els.messageCount.textContent = `${count} / ${Glow.MAX_MESSAGE_LENGTH}`; }; - Glow.updateDocumentTitle = () => { if (!Glow.state.unreadMessages) { document.title = Glow.state.originalTitle; return; } - document.title = `(${Glow.state.unreadMessages})` + Glow.state.originalTitle; + document.title = `(${Glow.state.unreadMessages}) ${Glow.state.originalTitle}`; }; - Glow.resetUnreadMessages = () => { Glow.state.unreadMessages = 0; Glow.updateDocumentTitle(); Glow.els.newMessagesBar.classList.add("hidden"); }; - Glow.showNewMessages = () => { Glow.els.newMessagesBar.classList.remove("hidden"); }; - Glow.normalizeSelectedFiles = fileList => { if (!fileList) return []; return [...fileList].filter(Boolean); }; - Glow.openDialog = ({ title, body, @@ -407,12 +398,10 @@ Glow.openDialog = ({ if (Glow.state.dialogResolver) { Glow.closeDialog(null); } - Glow.state.lastFocusedElement = document.activeElement; Glow.els.dialogTitle.textContent = title; Glow.els.dialogBody.replaceChildren(); Glow.els.dialogActions.replaceChildren(); - if (typeof body === "string") { const paragraph = document.createElement("p"); paragraph.textContent = body; @@ -420,20 +409,17 @@ Glow.openDialog = ({ } else if (body instanceof Node) { Glow.els.dialogBody.append(body); } - const fieldElements = {}; for (const field of fields) { const label = document.createElement("label"); label.className = "dialog-label"; label.textContent = field.label; - const isMultiline = Boolean(field.multiline); const input = document.createElement(isMultiline ? "textarea" : "input"); input.className = "field"; input.name = field.name; input.value = field.value || ""; input.placeholder = field.placeholder || ""; - if (isMultiline) { input.rows = field.rows || 3; input.maxLength = field.maxLength || 300; @@ -446,26 +432,20 @@ Glow.openDialog = ({ input.step = field.step || "1"; } } - label.append(input); Glow.els.dialogBody.append(label); fieldElements[field.name] = input; } - const cancel = document.createElement("button"); cancel.className = "button secondary"; cancel.type = "button"; cancel.textContent = cancelText; - const confirmButton = document.createElement("button"); confirmButton.className = danger ? "danger-button" : "button"; confirmButton.type = "button"; confirmButton.textContent = confirmText; - Glow.els.dialogActions.append(cancel, confirmButton); - Glow.els.dialogOverlay.classList.remove("hidden"); - return new Promise(resolve => { Glow.state.dialogResolver = resolve; cancel.addEventListener("click", () => Glow.closeDialog(null), { once: true }); @@ -486,7 +466,6 @@ Glow.openDialog = ({ }, 0); }); }; - Glow.closeDialog = (result = null) => { Glow.els.dialogOverlay.classList.add("hidden"); const resolve = Glow.state.dialogResolver;