Update static/js/04-main.js

This commit is contained in:
admin 2026-10-08 00:39:12 +00:00
parent d7fedb6295
commit b880591651

View File

@ -5,10 +5,8 @@ window.Glow = window.Glow || {};
Glow.loadRooms = async () => { Glow.loadRooms = async () => {
const previousSlug = Glow.state.activeRoom?.slug; const previousSlug = Glow.state.activeRoom?.slug;
const data = await Glow.apiRequest("/api/rooms"); const data = await Glow.apiRequest("/api/rooms");
Glow.state.rooms = data.rooms || []; Glow.state.rooms = data.rooms || [];
Glow.els.roomSelect.replaceChildren(); Glow.els.roomSelect.replaceChildren();
for (const room of Glow.state.rooms) { for (const room of Glow.state.rooms) {
const option = document.createElement("option"); const option = document.createElement("option");
option.value = room.slug; option.value = room.slug;
@ -16,20 +14,61 @@ Glow.loadRooms = async () => {
`${room.name}${room.visibility === "private" ? " (private)" : ""}`; `${room.name}${room.visibility === "private" ? " (private)" : ""}`;
Glow.els.roomSelect.append(option); Glow.els.roomSelect.append(option);
} }
Glow.els.roomSelect.disabled = !Glow.state.rooms.length; Glow.els.roomSelect.disabled = !Glow.state.rooms.length;
const selected = const selected =
Glow.state.rooms.find(room => room.slug === previousSlug) || Glow.state.rooms.find(room => room.slug === previousSlug) ||
Glow.state.rooms.find(room => room.slug === "general") || Glow.state.rooms.find(room => room.slug === "general") ||
Glow.state.rooms[0] || Glow.state.rooms[0] ||
null; null;
Glow.state.activeRoom = selected; Glow.state.activeRoom = selected;
Glow.updateViewUI(); Glow.updateViewUI();
Glow.updateChatControls(); Glow.updateChatControls();
}; };
Glow.renderEmojiAutocomplete = (input, query) => {
const box = Glow.els.emojiAutocomplete;
if (!box || !input) return;
const value = String(query || "").toLowerCase();
const entries = Object.entries(Glow.EMOJI_ALIASES)
.filter(([alias]) => alias.includes(value))
.slice(0, 8);
Glow.state.emojiSuggestions = entries;
if (!entries.length) {
box.classList.add("hidden");
box.replaceChildren();
return;
}
box.replaceChildren();
for (const [alias, emoji] of entries) {
const button = document.createElement("button");
button.className = "emoji-suggestion";
button.type = "button";
button.textContent = `${emoji} ::${alias}`;
button.addEventListener("click", () => {
const target = Glow.state.emojiActiveInput || input;
const current = target.value;
const replaced = current.replace(/::([a-z0-9_+-]*)$/i, `${emoji} `);
target.value = replaced;
target.dispatchEvent(new Event("input", { bubbles: true }));
box.classList.add("hidden");
target.focus();
});
box.append(button);
}
Glow.state.emojiActiveInput = input;
box.classList.remove("hidden");
};
Glow.maybeShowEmojiAutocomplete = input => {
const text = String(input?.value || "");
const match = text.match(/::([a-z0-9_+-]*)$/i);
if (!match) {
Glow.els.emojiAutocomplete?.classList.add("hidden");
return;
}
Glow.renderEmojiAutocomplete(input, match[1]);
};
Glow.updateViewUI = () => { Glow.updateViewUI = () => {
const view = Glow.state.activeView || "feed"; const view = Glow.state.activeView || "feed";
const room = Glow.state.activeRoom; const room = Glow.state.activeRoom;
@ -42,7 +81,6 @@ Glow.updateViewUI = () => {
const pageActive = view === "page"; const pageActive = view === "page";
const profileActive = view === "profile"; const profileActive = view === "profile";
const settingsActive = view === "settings"; const settingsActive = view === "settings";
Glow.els.feedBox.classList.toggle("hidden", !feedActive); Glow.els.feedBox.classList.toggle("hidden", !feedActive);
Glow.els.dmBox?.classList.toggle("hidden", !dmListActive); Glow.els.dmBox?.classList.toggle("hidden", !dmListActive);
Glow.els.chatBox.classList.toggle("hidden", !chatActive); Glow.els.chatBox.classList.toggle("hidden", !chatActive);
@ -52,7 +90,6 @@ Glow.updateViewUI = () => {
Glow.els.profileBox?.classList.toggle("hidden", !(profileActive || settingsActive)); Glow.els.profileBox?.classList.toggle("hidden", !(profileActive || settingsActive));
Glow.els.composerWrap.classList.toggle("hidden", !chatActive); Glow.els.composerWrap.classList.toggle("hidden", !chatActive);
Glow.els.threadComposerWrap?.classList.toggle("hidden", !threadActive); Glow.els.threadComposerWrap?.classList.toggle("hidden", !threadActive);
if (!chatActive) { if (!chatActive) {
Glow.state.typingUsers = []; Glow.state.typingUsers = [];
Glow.state.dmTypingUsers = []; Glow.state.dmTypingUsers = [];
@ -61,25 +98,19 @@ Glow.updateViewUI = () => {
Glow.els.typingBar.classList.add("hidden"); Glow.els.typingBar.classList.add("hidden");
} }
} }
Glow.els.feedButton.classList.toggle("active", feedActive); Glow.els.feedButton.classList.toggle("active", feedActive);
Glow.els.feedButton.setAttribute("aria-pressed", String(feedActive)); Glow.els.feedButton.setAttribute("aria-pressed", String(feedActive));
Glow.els.pagesButton?.classList.toggle("active", pagesActive || pageActive); Glow.els.pagesButton?.classList.toggle("active", pagesActive || pageActive);
Glow.els.pagesButton?.setAttribute("aria-pressed", String(pagesActive || pageActive)); Glow.els.pagesButton?.setAttribute("aria-pressed", String(pagesActive || pageActive));
Glow.els.dmsButton?.classList.toggle("active", dmListActive || view === "dm"); Glow.els.dmsButton?.classList.toggle("active", dmListActive || view === "dm");
Glow.els.dmsButton?.setAttribute("aria-pressed", String(dmListActive || view === "dm")); Glow.els.dmsButton?.setAttribute("aria-pressed", String(dmListActive || view === "dm"));
Glow.els.backButton?.classList.toggle( Glow.els.backButton?.classList.toggle(
"hidden", "hidden",
!(threadActive || pageActive || profileActive || settingsActive || view === "dm") !(threadActive || pageActive || profileActive || settingsActive || view === "dm")
); );
Glow.els.roomSelect.classList.toggle("active", chatActive && !dm); Glow.els.roomSelect.classList.toggle("active", chatActive && !dm);
Glow.els.teamSelect?.classList.toggle("hidden", dmListActive || view === "dm"); Glow.els.teamSelect?.classList.toggle("hidden", dmListActive || view === "dm");
Glow.els.roomSelect?.classList.toggle("hidden", dmListActive || view === "dm"); Glow.els.roomSelect?.classList.toggle("hidden", dmListActive || view === "dm");
if (feedActive) { if (feedActive) {
Glow.els.headerTitle.textContent = "Feed"; Glow.els.headerTitle.textContent = "Feed";
Glow.els.headerSubtitle.textContent = Glow.state.currentUser Glow.els.headerSubtitle.textContent = Glow.state.currentUser
@ -89,7 +120,6 @@ Glow.updateViewUI = () => {
Glow.els.presenceLabel?.classList.add("hidden"); Glow.els.presenceLabel?.classList.add("hidden");
return; return;
} }
if (dmListActive) { if (dmListActive) {
Glow.els.headerTitle.textContent = "Direct messages"; Glow.els.headerTitle.textContent = "Direct messages";
Glow.els.headerSubtitle.textContent = Glow.state.currentUser Glow.els.headerSubtitle.textContent = Glow.state.currentUser
@ -98,7 +128,6 @@ Glow.updateViewUI = () => {
Glow.els.presenceLabel?.classList.add("hidden"); Glow.els.presenceLabel?.classList.add("hidden");
return; return;
} }
if (threadActive) { if (threadActive) {
Glow.els.headerTitle.textContent = "Thread"; Glow.els.headerTitle.textContent = "Thread";
Glow.els.headerSubtitle.textContent = Glow.els.headerSubtitle.textContent =
@ -106,21 +135,18 @@ Glow.updateViewUI = () => {
Glow.updateThreadControls?.(); Glow.updateThreadControls?.();
return; return;
} }
if (pagesActive) { if (pagesActive) {
Glow.els.headerTitle.textContent = "Pages"; Glow.els.headerTitle.textContent = "Pages";
Glow.els.headerSubtitle.textContent = "Shared knowledge and resources"; Glow.els.headerSubtitle.textContent = "Shared knowledge and resources";
Glow.els.presenceLabel?.classList.add("hidden"); Glow.els.presenceLabel?.classList.add("hidden");
return; return;
} }
if (pageActive) { if (pageActive) {
Glow.els.headerTitle.textContent = Glow.state.activePage?.title || "Page"; Glow.els.headerTitle.textContent = Glow.state.activePage?.title || "Page";
Glow.els.headerSubtitle.textContent = "Shared knowledge and resources"; Glow.els.headerSubtitle.textContent = "Shared knowledge and resources";
Glow.els.presenceLabel?.classList.add("hidden"); Glow.els.presenceLabel?.classList.add("hidden");
return; return;
} }
if (profileActive) { if (profileActive) {
Glow.els.headerTitle.textContent = Glow.els.headerTitle.textContent =
Glow.state.activeProfile?.display_name || Glow.state.activeProfile?.display_name ||
@ -130,19 +156,19 @@ Glow.updateViewUI = () => {
Glow.els.presenceLabel?.classList.add("hidden"); Glow.els.presenceLabel?.classList.add("hidden");
return; return;
} }
if (settingsActive) { if (settingsActive) {
Glow.els.headerTitle.textContent = "Settings"; Glow.els.headerTitle.textContent = "Settings";
Glow.els.headerSubtitle.textContent = "Appearance, profile, and preferences"; Glow.els.headerSubtitle.textContent = "Appearance, profile, and preferences";
Glow.els.presenceLabel?.classList.add("hidden"); Glow.els.presenceLabel?.classList.add("hidden");
return; return;
} }
if (view === "dm" && dm) { if (view === "dm" && dm) {
const peer = const peer =
(dm.members || []).find( (dm.members || []).find(
member => Number(member.id) !== Number(Glow.state.currentUser?.id) member => Number(member.id) !== Number(Glow.state.currentUser?.id)
) || dm.members?.[0]; ) ||
dm.other_user ||
dm.members?.[0];
Glow.els.headerTitle.textContent = Glow.els.headerTitle.textContent =
peer?.display_name || peer?.username || "Direct message"; peer?.display_name || peer?.username || "Direct message";
Glow.els.headerSubtitle.textContent = Glow.els.headerSubtitle.textContent =
@ -151,7 +177,6 @@ Glow.updateViewUI = () => {
Glow.updateChatControls(); Glow.updateChatControls();
return; return;
} }
if (room) { if (room) {
Glow.els.headerTitle.textContent = room.name; Glow.els.headerTitle.textContent = room.name;
Glow.els.headerSubtitle.textContent = Glow.els.headerSubtitle.textContent =
@ -162,32 +187,24 @@ Glow.updateViewUI = () => {
Glow.els.headerTitle.textContent = "No rooms"; Glow.els.headerTitle.textContent = "No rooms";
Glow.els.headerSubtitle.textContent = "No accessible chat rooms"; Glow.els.headerSubtitle.textContent = "No accessible chat rooms";
} }
Glow.updateChatControls(); Glow.updateChatControls();
}; };
Glow.updateAuthUI = () => { Glow.updateAuthUI = () => {
const user = Glow.state.currentUser; const user = Glow.state.currentUser;
const authenticated = Boolean(user); const authenticated = Boolean(user);
Glow.els.authOverlay.classList.toggle("hidden", authenticated); Glow.els.authOverlay.classList.toggle("hidden", authenticated);
Glow.els.logoutButton.classList.toggle("hidden", !authenticated); Glow.els.logoutButton.classList.toggle("hidden", !authenticated);
Glow.els.headerAccount.textContent = Glow.els.headerAccount.textContent =
user ? `${user.username} · ${user.role}` : "not signed in"; user ? `${user.username} · ${user.role}` : "not signed in";
document.body.classList.toggle("is-moderator", Glow.canModerate()); document.body.classList.toggle("is-moderator", Glow.canModerate());
document.body.classList.toggle("is-admin", Glow.isAdmin()); document.body.classList.toggle("is-admin", Glow.isAdmin());
Glow.els.adminFab.classList.toggle("visible", Glow.canModerate()); Glow.els.adminFab.classList.toggle("visible", Glow.canModerate());
Glow.els.presenceDot.classList.toggle("online", authenticated); Glow.els.presenceDot.classList.toggle("online", authenticated);
Glow.els.presenceDot.classList.toggle("new", Boolean(user?.status === "restricted")); Glow.els.presenceDot.classList.toggle("new", Boolean(user?.status === "restricted"));
if (user?.status === "restricted") { if (user?.status === "restricted") {
Glow.showToast("Your account is temporarily restricted.", "error"); Glow.showToast("Your account is temporarily restricted.", "error");
} }
Glow.updateViewUI(); Glow.updateViewUI();
Glow.updateChatControls(); Glow.updateChatControls();
Glow.updateThreadControls?.(); Glow.updateThreadControls?.();
@ -198,7 +215,6 @@ Glow.checkInvite = async () => {
const params = new URLSearchParams(window.location.search); const params = new URLSearchParams(window.location.search);
const roomToken = params.get("invite"); const roomToken = params.get("invite");
const teamToken = params.get("team_invite"); const teamToken = params.get("team_invite");
if (roomToken) { if (roomToken) {
try { try {
const data = await Glow.apiRequest( const data = await Glow.apiRequest(
@ -218,7 +234,6 @@ Glow.checkInvite = async () => {
} }
} }
} }
if (teamToken) { if (teamToken) {
try { try {
const data = await Glow.apiAcceptTeamInvite(teamToken); const data = await Glow.apiAcceptTeamInvite(teamToken);
@ -238,18 +253,14 @@ Glow.checkAuth = async () => {
const data = await Glow.apiRequest("/api/me"); const data = await Glow.apiRequest("/api/me");
Glow.state.currentUser = data.authenticated ? data.user : null; Glow.state.currentUser = data.authenticated ? data.user : null;
Glow.state.csrfToken = data.csrf_token || ""; Glow.state.csrfToken = data.csrf_token || "";
Glow.updateAuthUI(); Glow.updateAuthUI();
await Glow.checkInvite(); await Glow.checkInvite();
await Glow.loadRooms(); await Glow.loadRooms();
await Glow.loadDms?.().catch(() => {}); await Glow.loadDms?.().catch(() => {});
Glow.state.activeView = "feed"; Glow.state.activeView = "feed";
Glow.updateViewUI(); Glow.updateViewUI();
await Glow.loadFeed(); await Glow.loadFeed();
await Glow.loadMySettings?.().catch(() => {}); await Glow.loadMySettings?.().catch(() => {});
Glow.calls?.scheduleInvitePolling?.(); Glow.calls?.scheduleInvitePolling?.();
} catch (error) { } catch (error) {
Glow.showToast(error.message || "Could not connect.", "error"); Glow.showToast(error.message || "Could not connect.", "error");
@ -263,38 +274,30 @@ Glow.authenticate = async endpoint => {
const accessKey = Glow.els.accessKeyInput.value; const accessKey = Glow.els.accessKeyInput.value;
const username = Glow.els.usernameInput.value.trim().toLowerCase(); const username = Glow.els.usernameInput.value.trim().toLowerCase();
const password = Glow.els.passwordInput.value; const password = Glow.els.passwordInput.value;
if (!accessKey || !username || !password) { if (!accessKey || !username || !password) {
Glow.showAuthNotice("Enter the private access key, username, and password."); Glow.showAuthNotice("Enter the private access key, username, and password.");
return; return;
} }
Glow.els.loginButton.disabled = true; Glow.els.loginButton.disabled = true;
Glow.els.registerButton.disabled = true; Glow.els.registerButton.disabled = true;
try { try {
const data = await Glow.apiRequest(endpoint, { const data = await Glow.apiRequest(endpoint, {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
body: JSON.stringify({ access_key: accessKey, username, password }) body: JSON.stringify({ access_key: accessKey, username, password })
}); });
Glow.state.currentUser = data.user; Glow.state.currentUser = data.user;
Glow.state.csrfToken = data.csrf_token || ""; Glow.state.csrfToken = data.csrf_token || "";
Glow.els.passwordInput.value = ""; Glow.els.passwordInput.value = "";
Glow.updateAuthUI(); Glow.updateAuthUI();
await Glow.checkInvite(); await Glow.checkInvite();
await Glow.loadRooms(); await Glow.loadRooms();
await Glow.loadDms?.().catch(() => {}); await Glow.loadDms?.().catch(() => {});
Glow.state.activeView = "feed"; Glow.state.activeView = "feed";
Glow.updateViewUI(); Glow.updateViewUI();
await Glow.loadFeed(); await Glow.loadFeed();
await Glow.loadMySettings?.().catch(() => {}); await Glow.loadMySettings?.().catch(() => {});
Glow.calls?.scheduleInvitePolling?.(); Glow.calls?.scheduleInvitePolling?.();
Glow.showToast( Glow.showToast(
endpoint.endsWith("register") ? "Account created." : "Signed in." endpoint.endsWith("register") ? "Account created." : "Signed in."
); );
@ -315,7 +318,6 @@ Glow.logout = async () => {
try { try {
await Glow.apiRequest("/api/logout", { method: "POST" }); await Glow.apiRequest("/api/logout", { method: "POST" });
} catch (_) {} } catch (_) {}
Glow.state.currentUser = null; Glow.state.currentUser = null;
Glow.state.csrfToken = ""; Glow.state.csrfToken = "";
Glow.state.rooms = []; Glow.state.rooms = [];
@ -332,7 +334,6 @@ Glow.logout = async () => {
Glow.state.activePage = null; Glow.state.activePage = null;
Glow.state.activeProfile = null; Glow.state.activeProfile = null;
Glow.state.settings = null; Glow.state.settings = null;
Glow.clearMessages(); Glow.clearMessages();
Glow.clearFeed(); Glow.clearFeed();
Glow.clearDms(); Glow.clearDms();
@ -340,17 +341,12 @@ Glow.logout = async () => {
Glow.closeReactionPopup?.(); Glow.closeReactionPopup?.();
Glow.closeMediaModal?.(); Glow.closeMediaModal?.();
Glow.endLocalCallUi?.(); Glow.endLocalCallUi?.();
clearTimeout(Glow.state.calls?.invitePollTimer); clearTimeout(Glow.state.calls?.invitePollTimer);
Glow.updateAuthUI(); Glow.updateAuthUI();
Glow.els.authOverlay.classList.remove("hidden"); Glow.els.authOverlay.classList.remove("hidden");
Glow.els.accessKeyInput.focus(); Glow.els.accessKeyInput.focus();
await Glow.refreshCsrfToken().catch(() => {}); await Glow.refreshCsrfToken().catch(() => {});
await Glow.loadFeed().catch(() => {}); await Glow.loadFeed().catch(() => {});
Glow.showToast("Signed out."); Glow.showToast("Signed out.");
}; };
@ -359,7 +355,6 @@ Glow.toggleNotifications = async () => {
Glow.showToast("Browser notifications are not supported.", "error"); Glow.showToast("Browser notifications are not supported.", "error");
return; return;
} }
if (!Glow.state.notificationsEnabled) { if (!Glow.state.notificationsEnabled) {
const permission = await Notification.requestPermission(); const permission = await Notification.requestPermission();
if (permission !== "granted") { if (permission !== "granted") {
@ -367,7 +362,6 @@ Glow.toggleNotifications = async () => {
return; return;
} }
} }
Glow.state.notificationsEnabled = !Glow.state.notificationsEnabled; Glow.state.notificationsEnabled = !Glow.state.notificationsEnabled;
localStorage.setItem( localStorage.setItem(
"glow_notifications_enabled", "glow_notifications_enabled",
@ -402,19 +396,16 @@ Glow.toggleTypingIndicators = () => {
Glow.pingPresence = async () => { Glow.pingPresence = async () => {
if (!Glow.state.currentUser) return; if (!Glow.state.currentUser) return;
const payload = { const payload = {
view: Glow.state.activeView view: Glow.state.activeView
}; };
if (Glow.state.activeDm?.id) { if (Glow.state.activeDm?.id) {
payload.direct_conversation_id = Glow.state.activeDm.id; payload.dm_id = Glow.state.activeDm.id;
} else if (Glow.state.activeChannel?.id) { } else if (Glow.state.activeChannel?.id) {
payload.channel_id = Glow.state.activeChannel.id; payload.channel_id = Glow.state.activeChannel.id;
} else if (Glow.state.activeRoom?.id) { } else if (Glow.state.activeRoom?.id) {
payload.room_id = Glow.state.activeRoom.id; payload.room_id = Glow.state.activeRoom.id;
} }
try { try {
await Glow.apiPingPresence(payload); await Glow.apiPingPresence(payload);
} catch (_) {} } catch (_) {}
@ -429,7 +420,6 @@ Glow.refreshPresence = async () => {
Glow.els.presenceLabel.classList.remove("hidden"); Glow.els.presenceLabel.classList.remove("hidden");
return; return;
} }
if (Glow.state.activeTeam?.slug && Glow.state.activeChannel?.slug) { if (Glow.state.activeTeam?.slug && Glow.state.activeChannel?.slug) {
const data = await Glow.apiGetChannelPresence( const data = await Glow.apiGetChannelPresence(
Glow.state.activeTeam.slug, Glow.state.activeTeam.slug,
@ -440,7 +430,6 @@ Glow.refreshPresence = async () => {
Glow.els.presenceLabel.classList.remove("hidden"); Glow.els.presenceLabel.classList.remove("hidden");
return; return;
} }
if (Glow.state.activeRoom?.slug) { if (Glow.state.activeRoom?.slug) {
const data = await Glow.apiGetRoomPresence(Glow.state.activeRoom.slug); const data = await Glow.apiGetRoomPresence(Glow.state.activeRoom.slug);
Glow.state.presence.roomOnlineCount = Number(data.online_count) || 0; Glow.state.presence.roomOnlineCount = Number(data.online_count) || 0;
@ -448,7 +437,6 @@ Glow.refreshPresence = async () => {
Glow.els.presenceLabel.classList.remove("hidden"); Glow.els.presenceLabel.classList.remove("hidden");
return; return;
} }
Glow.els.presenceLabel.classList.add("hidden"); Glow.els.presenceLabel.classList.add("hidden");
} catch (_) { } catch (_) {
Glow.els.presenceLabel?.classList.add("hidden"); Glow.els.presenceLabel?.classList.add("hidden");
@ -458,7 +446,6 @@ Glow.refreshPresence = async () => {
Glow.schedulePoll = () => { Glow.schedulePoll = () => {
clearTimeout(Glow.state.pollTimer); clearTimeout(Glow.state.pollTimer);
if (Glow.els.blockedOverlay.classList.contains("hidden") === false) return; if (Glow.els.blockedOverlay.classList.contains("hidden") === false) return;
Glow.state.pollTimer = setTimeout(async () => { Glow.state.pollTimer = setTimeout(async () => {
if (Glow.state.activeView === "feed") { if (Glow.state.activeView === "feed") {
await Glow.pollFeed(); await Glow.pollFeed();
@ -475,7 +462,6 @@ Glow.schedulePoll = () => {
} else if (Glow.state.activeView === "dms") { } else if (Glow.state.activeView === "dms") {
await Glow.loadDms?.().catch(() => {}); await Glow.loadDms?.().catch(() => {});
} }
await Glow.pingPresence?.(); await Glow.pingPresence?.();
await Glow.refreshPresence?.(); await Glow.refreshPresence?.();
Glow.schedulePoll(); Glow.schedulePoll();
@ -484,10 +470,10 @@ Glow.schedulePoll = () => {
Glow.els.chatForm.addEventListener("submit", Glow.sendMessage); Glow.els.chatForm.addEventListener("submit", Glow.sendMessage);
Glow.els.threadForm?.addEventListener("submit", event => Glow.sendThreadReply?.(event)); Glow.els.threadForm?.addEventListener("submit", event => Glow.sendThreadReply?.(event));
Glow.els.messageInput.addEventListener("input", () => { Glow.els.messageInput.addEventListener("input", () => {
Glow.autoResizeComposer(); Glow.autoResizeComposer();
Glow.updateMessageCount(); Glow.updateMessageCount();
Glow.maybeShowEmojiAutocomplete?.(Glow.els.messageInput);
clearTimeout(Glow.state.typingTimer); clearTimeout(Glow.state.typingTimer);
if (Glow.els.messageInput.value.trim()) { if (Glow.els.messageInput.value.trim()) {
Glow.sendTyping(true); Glow.sendTyping(true);
@ -498,26 +484,23 @@ Glow.els.messageInput.addEventListener("input", () => {
Glow.sendTyping(false); Glow.sendTyping(false);
} }
}); });
Glow.els.threadInput?.addEventListener("input", () => { Glow.els.threadInput?.addEventListener("input", () => {
Glow.autoResizeThreadComposer?.(); Glow.autoResizeThreadComposer?.();
Glow.updateThreadMessageCount?.(); Glow.updateThreadMessageCount?.();
Glow.maybeShowEmojiAutocomplete?.(Glow.els.threadInput);
}); });
Glow.els.messageInput.addEventListener("keydown", event => { Glow.els.messageInput.addEventListener("keydown", event => {
if (event.key === "Enter" && !event.shiftKey) { if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault(); event.preventDefault();
Glow.sendMessage(event); Glow.sendMessage(event);
} }
}); });
Glow.els.threadInput?.addEventListener("keydown", event => { Glow.els.threadInput?.addEventListener("keydown", event => {
if (event.key === "Enter" && !event.shiftKey) { if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault(); event.preventDefault();
Glow.sendThreadReply?.(event); Glow.sendThreadReply?.(event);
} }
}); });
Glow.els.attachmentInput.addEventListener("change", () => { Glow.els.attachmentInput.addEventListener("change", () => {
const files = Glow.normalizeSelectedFiles(Glow.els.attachmentInput.files); const files = Glow.normalizeSelectedFiles(Glow.els.attachmentInput.files);
if (!files.length) { if (!files.length) {
@ -533,7 +516,6 @@ Glow.els.attachmentInput.addEventListener("change", () => {
} }
Glow.setSelectedAttachment(files); Glow.setSelectedAttachment(files);
}); });
Glow.els.threadAttachmentInput?.addEventListener("change", () => { Glow.els.threadAttachmentInput?.addEventListener("change", () => {
const files = Glow.normalizeSelectedFiles(Glow.els.threadAttachmentInput.files); const files = Glow.normalizeSelectedFiles(Glow.els.threadAttachmentInput.files);
if (!files.length) { if (!files.length) {
@ -549,20 +531,16 @@ Glow.els.threadAttachmentInput?.addEventListener("change", () => {
} }
Glow.setSelectedThreadAttachment?.(files); Glow.setSelectedThreadAttachment?.(files);
}); });
Glow.els.feedButton.addEventListener("click", Glow.openFeed); Glow.els.feedButton.addEventListener("click", Glow.openFeed);
Glow.els.dmsButton?.addEventListener("click", async () => { Glow.els.dmsButton?.addEventListener("click", async () => {
Glow.state.activeView = "dms"; Glow.state.activeView = "dms";
Glow.updateViewUI(); Glow.updateViewUI();
await Glow.loadDms?.(); await Glow.loadDms?.();
Glow.schedulePoll(); Glow.schedulePoll();
}); });
Glow.els.pagesButton?.addEventListener("click", () => { Glow.els.pagesButton?.addEventListener("click", () => {
Glow.openPages?.(); Glow.openPages?.();
}); });
Glow.els.backButton?.addEventListener("click", async () => { Glow.els.backButton?.addEventListener("click", async () => {
if (Glow.state.activeView === "thread") { if (Glow.state.activeView === "thread") {
await Glow.returnToChannel?.(); await Glow.returnToChannel?.();
@ -576,30 +554,24 @@ Glow.els.backButton?.addEventListener("click", async () => {
await Glow.loadDms?.(); await Glow.loadDms?.();
} }
}); });
Glow.els.loginButton.addEventListener("click", () => { Glow.els.loginButton.addEventListener("click", () => {
Glow.authenticate("/api/login"); Glow.authenticate("/api/login");
}); });
Glow.els.registerButton.addEventListener("click", () => { Glow.els.registerButton.addEventListener("click", () => {
Glow.authenticate("/api/register"); Glow.authenticate("/api/register");
}); });
Glow.els.passwordInput.addEventListener("keydown", event => { Glow.els.passwordInput.addEventListener("keydown", event => {
if (event.key === "Enter") { if (event.key === "Enter") {
event.preventDefault(); event.preventDefault();
Glow.authenticate("/api/login"); Glow.authenticate("/api/login");
} }
}); });
Glow.els.logoutButton.addEventListener("click", Glow.logout); Glow.els.logoutButton.addEventListener("click", Glow.logout);
Glow.els.roomSelect.addEventListener("change", async () => { Glow.els.roomSelect.addEventListener("change", async () => {
const selected = Glow.state.rooms.find( const selected = Glow.state.rooms.find(
room => room.slug === Glow.els.roomSelect.value room => room.slug === Glow.els.roomSelect.value
); );
if (!selected) return; if (!selected) return;
Glow.state.activeDm = null; Glow.state.activeDm = null;
Glow.state.activeRoom = selected; Glow.state.activeRoom = selected;
Glow.state.activeView = "room"; Glow.state.activeView = "room";
@ -607,20 +579,17 @@ Glow.els.roomSelect.addEventListener("change", async () => {
Glow.state.dmTypingUsers = []; Glow.state.dmTypingUsers = [];
Glow.renderTyping(); Glow.renderTyping();
Glow.updateViewUI(); Glow.updateViewUI();
await Glow.loadMessages(); await Glow.loadMessages();
Glow.pollTyping(); Glow.pollTyping();
Glow.pingPresence?.(); Glow.pingPresence?.();
Glow.refreshPresence?.(); Glow.refreshPresence?.();
Glow.schedulePoll(); Glow.schedulePoll();
}); });
Glow.els.teamSelect?.addEventListener("change", () => { Glow.els.teamSelect?.addEventListener("change", () => {
const teams = Glow.state.teams || []; const teams = Glow.state.teams || [];
Glow.state.activeTeam = Glow.state.activeTeam =
teams.find(team => team.slug === Glow.els.teamSelect.value) || null; teams.find(team => team.slug === Glow.els.teamSelect.value) || null;
}); });
Glow.els.adminFab.addEventListener("click", Glow.openAdminPanel); Glow.els.adminFab.addEventListener("click", Glow.openAdminPanel);
Glow.els.closeAdminButton.addEventListener("click", Glow.closeAdminPanel); Glow.els.closeAdminButton.addEventListener("click", Glow.closeAdminPanel);
Glow.els.createRoomButton.addEventListener("click", Glow.createRoom); Glow.els.createRoomButton.addEventListener("click", Glow.createRoom);
@ -628,121 +597,99 @@ Glow.els.clearMessagesButton.addEventListener("click", Glow.clearActiveRoom);
Glow.els.typingToggleButton.addEventListener("click", Glow.toggleTypingIndicators); Glow.els.typingToggleButton.addEventListener("click", Glow.toggleTypingIndicators);
Glow.els.notificationsToggleButton.addEventListener("click", Glow.toggleNotifications); Glow.els.notificationsToggleButton.addEventListener("click", Glow.toggleNotifications);
Glow.els.themeButton?.addEventListener("click", Glow.openAccentColorPicker); Glow.els.themeButton?.addEventListener("click", Glow.openAccentColorPicker);
Glow.els.settingsButton?.addEventListener("click", () => { Glow.els.settingsButton?.addEventListener("click", () => {
Glow.toggleHeaderMenu?.(); Glow.toggleHeaderMenu?.();
}); });
Glow.els.jumpToLatestButton.addEventListener("click", () => { Glow.els.jumpToLatestButton.addEventListener("click", () => {
Glow.els.chatBox.scrollTop = Glow.els.chatBox.scrollHeight; Glow.els.chatBox.scrollTop = Glow.els.chatBox.scrollHeight;
Glow.resetUnreadMessages(); Glow.resetUnreadMessages();
}); });
Glow.els.chatBox.addEventListener("scroll", () => { Glow.els.chatBox.addEventListener("scroll", () => {
if (Glow.isNearBottom()) { if (Glow.isNearBottom()) {
Glow.resetUnreadMessages(); Glow.resetUnreadMessages();
} }
}); });
Glow.els.adminPanel.addEventListener("click", event => { Glow.els.adminPanel.addEventListener("click", event => {
if (event.target === Glow.els.adminPanel) { if (event.target === Glow.els.adminPanel) {
Glow.closeAdminPanel(); Glow.closeAdminPanel();
} }
}); });
Glow.els.dialogOverlay.addEventListener("click", event => { Glow.els.dialogOverlay.addEventListener("click", event => {
if (event.target === Glow.els.dialogOverlay) { if (event.target === Glow.els.dialogOverlay) {
Glow.closeDialog(null); Glow.closeDialog(null);
} }
}); });
Glow.els.headerMenuOverlay?.addEventListener("click", event => { Glow.els.headerMenuOverlay?.addEventListener("click", event => {
if (event.target === Glow.els.headerMenuOverlay) { if (event.target === Glow.els.headerMenuOverlay) {
Glow.closeHeaderMenu?.(); Glow.closeHeaderMenu?.();
} }
}); });
Glow.els.closeReactionPopupButton?.addEventListener("click", () => { Glow.els.closeReactionPopupButton?.addEventListener("click", () => {
Glow.closeReactionPopup?.(); Glow.closeReactionPopup?.();
}); });
Glow.els.reactionPopupOverlay?.addEventListener("click", event => { Glow.els.reactionPopupOverlay?.addEventListener("click", event => {
if (event.target === Glow.els.reactionPopupOverlay) { if (event.target === Glow.els.reactionPopupOverlay) {
Glow.closeReactionPopup?.(); Glow.closeReactionPopup?.();
} }
}); });
Glow.els.closeMediaButton?.addEventListener("click", () => { Glow.els.closeMediaButton?.addEventListener("click", () => {
Glow.closeMediaModal?.(); Glow.closeMediaModal?.();
}); });
Glow.els.mediaOverlay?.addEventListener("click", event => { Glow.els.mediaOverlay?.addEventListener("click", event => {
if (event.target === Glow.els.mediaOverlay) { if (event.target === Glow.els.mediaOverlay) {
Glow.closeMediaModal?.(); Glow.closeMediaModal?.();
} }
}); });
Glow.els.closeCallPanelButton?.addEventListener("click", () => { Glow.els.closeCallPanelButton?.addEventListener("click", () => {
Glow.els.callOverlay?.classList.add("hidden"); Glow.els.callOverlay?.classList.add("hidden");
}); });
document.addEventListener("keydown", event => { document.addEventListener("keydown", event => {
if (event.key !== "Escape") return; if (event.key === "Escape") {
if (!Glow.els.dialogOverlay.classList.contains("hidden")) {
if (!Glow.els.dialogOverlay.classList.contains("hidden")) { Glow.closeDialog(null);
Glow.closeDialog(null); return;
return; }
} if (!Glow.els.mediaOverlay?.classList.contains("hidden")) {
Glow.closeMediaModal?.();
if (!Glow.els.mediaOverlay?.classList.contains("hidden")) { return;
Glow.closeMediaModal?.(); }
return; if (!Glow.els.reactionPopupOverlay?.classList.contains("hidden")) {
} Glow.closeReactionPopup?.();
return;
if (!Glow.els.reactionPopupOverlay?.classList.contains("hidden")) { }
Glow.closeReactionPopup?.(); if (!Glow.els.headerMenuOverlay?.classList.contains("hidden")) {
return; Glow.closeHeaderMenu?.();
} return;
}
if (!Glow.els.headerMenuOverlay?.classList.contains("hidden")) { if (!Glow.els.callOverlay?.classList.contains("hidden")) {
Glow.closeHeaderMenu?.(); Glow.els.callOverlay.classList.add("hidden");
return; return;
} }
Glow.els.emojiAutocomplete?.classList.add("hidden");
if (!Glow.els.callOverlay?.classList.contains("hidden")) { if (Glow.state.activeView === "thread") {
Glow.els.callOverlay.classList.add("hidden"); Glow.returnToChannel?.();
return; return;
} }
if (Glow.state.activeView === "page") {
if (Glow.state.activeView === "thread") { Glow.openPages?.();
Glow.returnToChannel?.(); return;
return; }
} if (Glow.state.activeView === "dm") {
Glow.state.activeView = "dms";
if (Glow.state.activeView === "page") { Glow.updateViewUI();
Glow.openPages?.(); Glow.loadDms?.();
return; return;
} }
if (Glow.els.adminPanel.classList.contains("open")) {
if (Glow.state.activeView === "dm") { Glow.closeAdminPanel();
Glow.state.activeView = "dms"; }
Glow.updateViewUI();
Glow.loadDms?.();
return;
}
if (Glow.els.adminPanel.classList.contains("open")) {
Glow.closeAdminPanel();
} }
}); });
document.addEventListener("visibilitychange", () => { document.addEventListener("visibilitychange", () => {
Glow.schedulePoll(); Glow.schedulePoll();
Glow.calls?.scheduleInvitePolling?.(); Glow.calls?.scheduleInvitePolling?.();
if (!document.hidden) { if (!document.hidden) {
Glow.resetUnreadMessages(); Glow.resetUnreadMessages();
if (Glow.state.activeView === "feed") { if (Glow.state.activeView === "feed") {
Glow.pollFeed(); Glow.pollFeed();
} else if ( } else if (
@ -759,15 +706,12 @@ document.addEventListener("visibilitychange", () => {
} else if (Glow.state.activeView === "dms") { } else if (Glow.state.activeView === "dms") {
Glow.loadDms?.(); Glow.loadDms?.();
} }
Glow.pingPresence?.(); Glow.pingPresence?.();
Glow.refreshPresence?.(); Glow.refreshPresence?.();
} }
}); });
window.addEventListener("online", () => { window.addEventListener("online", () => {
Glow.showToast("Back online."); Glow.showToast("Back online.");
if (Glow.state.activeView === "feed") { if (Glow.state.activeView === "feed") {
Glow.pollFeed(); Glow.pollFeed();
} else if ( } else if (
@ -784,16 +728,13 @@ window.addEventListener("online", () => {
} else if (Glow.state.activeView === "dms") { } else if (Glow.state.activeView === "dms") {
Glow.loadDms?.(); Glow.loadDms?.();
} }
Glow.pingPresence?.(); Glow.pingPresence?.();
Glow.refreshPresence?.(); Glow.refreshPresence?.();
Glow.calls?.scheduleInvitePolling?.(); Glow.calls?.scheduleInvitePolling?.();
}); });
window.addEventListener("offline", () => { window.addEventListener("offline", () => {
Glow.showToast("You are offline.", "error"); Glow.showToast("You are offline.", "error");
}); });
setInterval(() => { setInterval(() => {
if ( if (
Glow.state.activeView === "room" || Glow.state.activeView === "room" ||
@ -803,7 +744,6 @@ setInterval(() => {
Glow.pollTyping(); Glow.pollTyping();
} }
}, 2500); }, 2500);
setInterval(() => { setInterval(() => {
if ( if (
Glow.state.activeView === "room" || Glow.state.activeView === "room" ||
@ -814,7 +754,6 @@ setInterval(() => {
Glow.refreshPresence?.(); Glow.refreshPresence?.();
} }
}, 10000); }, 10000);
Glow.applyAccentColor(Glow.state.accentColor); Glow.applyAccentColor(Glow.state.accentColor);
Glow.autoResizeComposer(); Glow.autoResizeComposer();
Glow.updateMessageCount(); Glow.updateMessageCount();