Update static/js/08-settings.js

This commit is contained in:
admin 2026-10-08 00:45:06 +00:00
parent 73f10ab2cb
commit f0d7f26c63

View File

@ -1,8 +1,6 @@
// static/js/08-settings.js // static/js/08-settings.js
"use strict"; "use strict";
window.Glow = window.Glow || {}; window.Glow = window.Glow || {};
Glow.state = Glow.state || {}; Glow.state = Glow.state || {};
Glow.state.settings = Glow.state.settings || null; Glow.state.settings = Glow.state.settings || null;
@ -14,13 +12,11 @@ Glow.applyThemeMode = mode => {
Glow.applyCustomCss = cssText => { Glow.applyCustomCss = cssText => {
let style = document.getElementById("glowCustomCss"); let style = document.getElementById("glowCustomCss");
if (!style) { if (!style) {
style = document.createElement("style"); style = document.createElement("style");
style.id = "glowCustomCss"; style.id = "glowCustomCss";
document.head.append(style); document.head.append(style);
} }
style.textContent = typeof cssText === "string" ? cssText : ""; style.textContent = typeof cssText === "string" ? cssText : "";
}; };
@ -28,15 +24,12 @@ Glow.loadMySettings = async () => {
try { try {
const data = await Glow.apiGetMySettings(); const data = await Glow.apiGetMySettings();
Glow.state.settings = data.settings || {}; Glow.state.settings = data.settings || {};
const accent = Glow.state.settings.accent_color || Glow.state.accentColor; const accent = Glow.state.settings.accent_color || Glow.state.accentColor;
Glow.applyAccentColor(accent); Glow.applyAccentColor(accent);
const mode = const mode =
Glow.state.settings.theme_mode || Glow.state.settings.theme_mode ||
localStorage.getItem("glow_theme_mode") || localStorage.getItem("glow_theme_mode") ||
"dark"; "dark";
Glow.applyThemeMode(mode); Glow.applyThemeMode(mode);
Glow.applyCustomCss(Glow.state.settings.custom_css || ""); Glow.applyCustomCss(Glow.state.settings.custom_css || "");
} catch (_) { } catch (_) {
@ -48,44 +41,34 @@ Glow.loadMySettings = async () => {
Glow.renderHeaderMenu = () => { Glow.renderHeaderMenu = () => {
const list = Glow.els.headerMenuList; const list = Glow.els.headerMenuList;
if (!list) return; if (!list) return;
list.replaceChildren(); list.replaceChildren();
const addItem = ({ label, hint = "", onClick, hidden = false }) => { const addItem = ({ label, hint = "", onClick, hidden = false }) => {
if (hidden) return; if (hidden) return;
const button = document.createElement("button"); const button = document.createElement("button");
button.className = "header-menu-item"; button.className = "header-menu-item";
button.type = "button"; button.type = "button";
const left = document.createElement("span"); const left = document.createElement("span");
left.textContent = label; left.textContent = label;
const right = document.createElement("small"); const right = document.createElement("small");
right.textContent = hint; right.textContent = hint;
button.append(left, right); button.append(left, right);
button.addEventListener("click", () => { button.addEventListener("click", () => {
Glow.closeHeaderMenu?.(); Glow.closeHeaderMenu?.();
onClick?.(); onClick?.();
}); });
list.append(button); list.append(button);
}; };
addItem({ addItem({
label: "My profile", label: "My profile",
hint: Glow.state.currentUser ? `@${Glow.state.currentUser.username}` : "", hint: Glow.state.currentUser ? `@${Glow.state.currentUser.username}` : "",
onClick: () => Glow.openMyProfile?.(), onClick: () => Glow.openMyProfile?.(),
hidden: !Glow.state.currentUser hidden: !Glow.state.currentUser
}); });
addItem({ addItem({
label: "Settings", label: "Settings",
hint: "Theme, CSS, profile", hint: "Theme, CSS, profile",
onClick: () => Glow.openSettings?.() onClick: () => Glow.openSettings?.()
}); });
addItem({ addItem({
label: "Direct messages", label: "Direct messages",
hint: Glow.state.currentUser ? "Private chat" : "", hint: Glow.state.currentUser ? "Private chat" : "",
@ -97,28 +80,24 @@ Glow.renderHeaderMenu = () => {
}, },
hidden: !Glow.state.currentUser hidden: !Glow.state.currentUser
}); });
addItem({ addItem({
label: "Create team", label: "Create team",
hint: "Up to 3 max", hint: "Up to 3 max",
onClick: () => Glow.openCreateTeamDialog?.(), onClick: () => Glow.openCreateTeamDialog?.(),
hidden: !Glow.state.currentUser hidden: !Glow.state.currentUser
}); });
addItem({ addItem({
label: "Edit current team", label: "Edit current team",
hint: Glow.state.activeTeam?.can_manage ? "Manage" : "", hint: Glow.state.activeTeam?.can_manage ? "Manage" : "",
onClick: () => Glow.openEditTeamDialog?.(Glow.state.activeTeam), onClick: () => Glow.openEditTeamDialog?.(Glow.state.activeTeam),
hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage
}); });
addItem({ addItem({
label: "Add channel", label: "Add channel",
hint: Glow.state.activeTeam?.name || "", hint: Glow.state.activeTeam?.name || "",
onClick: () => Glow.openCreateChannelDialog?.(Glow.state.activeTeam), onClick: () => Glow.openCreateChannelDialog?.(Glow.state.activeTeam),
hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage
}); });
addItem({ addItem({
label: "Edit current channel", label: "Edit current channel",
hint: Glow.state.activeChannel?.name || Glow.state.activeRoom?.name || "", hint: Glow.state.activeChannel?.name || Glow.state.activeRoom?.name || "",
@ -132,21 +111,18 @@ Glow.renderHeaderMenu = () => {
!(Glow.state.activeChannel || Glow.state.activeRoom) || !(Glow.state.activeChannel || Glow.state.activeRoom) ||
Glow.state.activeView === "dm" Glow.state.activeView === "dm"
}); });
addItem({ addItem({
label: "Manage team members", label: "Manage team members",
hint: Glow.state.activeTeam?.can_manage ? "Members" : "", hint: Glow.state.activeTeam?.can_manage ? "Members" : "",
onClick: () => Glow.manageTeamMembers?.(Glow.state.activeTeam), onClick: () => Glow.manageTeamMembers?.(Glow.state.activeTeam),
hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage
}); });
addItem({ addItem({
label: "Create team invite", label: "Create team invite",
hint: Glow.state.activeTeam?.can_manage ? "Share" : "", hint: Glow.state.activeTeam?.can_manage ? "Share" : "",
onClick: () => Glow.createTeamInviteLink?.(Glow.state.activeTeam), onClick: () => Glow.createTeamInviteLink?.(Glow.state.activeTeam),
hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage hidden: !Glow.state.activeTeam || !Glow.state.activeTeam.can_manage
}); });
addItem({ addItem({
label: "Delete current channel", label: "Delete current channel",
hint: Glow.state.activeChannel?.name || "", hint: Glow.state.activeChannel?.name || "",
@ -179,22 +155,18 @@ Glow.toggleHeaderMenu = () => {
Glow.openSettings = async () => { Glow.openSettings = async () => {
Glow.state.activeView = "settings"; Glow.state.activeView = "settings";
Glow.updateViewUI(); Glow.updateViewUI();
if (!Glow.state.settings && Glow.state.currentUser) { if (!Glow.state.settings && Glow.state.currentUser) {
await Glow.loadMySettings(); await Glow.loadMySettings();
} }
Glow.renderSettingsView(); Glow.renderSettingsView();
}; };
Glow.renderNameColorPreview = value => { Glow.renderNameColorPreview = value => {
const wrap = document.createElement("div"); const wrap = document.createElement("div");
wrap.className = "settings-row"; wrap.className = "settings-row";
const label = document.createElement("div"); const label = document.createElement("div");
label.className = "header-subtitle"; label.className = "header-subtitle";
label.textContent = "Preview"; label.textContent = "Preview";
const preview = document.createElement("div"); const preview = document.createElement("div");
preview.className = "name-color-preview"; preview.className = "name-color-preview";
preview.textContent = preview.textContent =
@ -202,7 +174,6 @@ Glow.renderNameColorPreview = value => {
preview.style.color = preview.style.color =
Glow.normalizeNameColor(value) || Glow.normalizeNameColor(value) ||
Glow.roleDefaultNameColor(Glow.state.currentUser?.role); Glow.roleDefaultNameColor(Glow.state.currentUser?.role);
wrap.append(label, preview); wrap.append(label, preview);
return wrap; return wrap;
}; };
@ -216,16 +187,13 @@ Glow.renderSettingsView = () => {
const appearance = document.createElement("section"); const appearance = document.createElement("section");
appearance.className = "settings-section"; appearance.className = "settings-section";
const aTitle = document.createElement("h2"); const aTitle = document.createElement("h2");
aTitle.textContent = "Appearance"; aTitle.textContent = "Appearance";
const aText = document.createElement("p"); const aText = document.createElement("p");
aText.textContent = "Set your theme mode, accent color, username color, and custom CSS."; aText.textContent = "Set your theme mode, accent color, username color, profile background, and banner color.";
const themeRow = document.createElement("div"); const themeRow = document.createElement("div");
themeRow.className = "settings-theme-row"; themeRow.className = "settings-theme-row";
const currentTheme = const currentTheme =
Glow.state.settings?.theme_mode || Glow.state.settings?.theme_mode ||
localStorage.getItem("glow_theme_mode") || localStorage.getItem("glow_theme_mode") ||
@ -235,7 +203,6 @@ Glow.renderSettingsView = () => {
darkButton.className = `theme-pill${currentTheme !== "light" ? " active" : ""}`; darkButton.className = `theme-pill${currentTheme !== "light" ? " active" : ""}`;
darkButton.type = "button"; darkButton.type = "button";
darkButton.textContent = "Dark"; darkButton.textContent = "Dark";
const lightButton = document.createElement("button"); const lightButton = document.createElement("button");
lightButton.className = `theme-pill${currentTheme === "light" ? " active" : ""}`; lightButton.className = `theme-pill${currentTheme === "light" ? " active" : ""}`;
lightButton.type = "button"; lightButton.type = "button";
@ -247,23 +214,19 @@ Glow.renderSettingsView = () => {
Glow.state.settings.theme_mode = "dark"; Glow.state.settings.theme_mode = "dark";
Glow.renderSettingsView(); Glow.renderSettingsView();
}); });
lightButton.addEventListener("click", () => { lightButton.addEventListener("click", () => {
Glow.applyThemeMode("light"); Glow.applyThemeMode("light");
Glow.state.settings ||= {}; Glow.state.settings ||= {};
Glow.state.settings.theme_mode = "light"; Glow.state.settings.theme_mode = "light";
Glow.renderSettingsView(); Glow.renderSettingsView();
}); });
themeRow.append(darkButton, lightButton); themeRow.append(darkButton, lightButton);
const accentWrap = document.createElement("div"); const accentWrap = document.createElement("div");
accentWrap.className = "settings-row"; accentWrap.className = "settings-row";
const accentLabel = document.createElement("label"); const accentLabel = document.createElement("label");
accentLabel.className = "dialog-label"; accentLabel.className = "dialog-label";
accentLabel.textContent = "Accent color"; accentLabel.textContent = "Accent color";
const accentInput = document.createElement("input"); const accentInput = document.createElement("input");
accentInput.type = "color"; accentInput.type = "color";
accentInput.className = "accent-color-input"; accentInput.className = "accent-color-input";
@ -271,23 +234,53 @@ Glow.renderSettingsView = () => {
Glow.normalizeAccentColor( Glow.normalizeAccentColor(
Glow.state.settings?.accent_color || Glow.state.accentColor Glow.state.settings?.accent_color || Glow.state.accentColor
) || Glow.DEFAULT_ACCENT_COLOR; ) || Glow.DEFAULT_ACCENT_COLOR;
accentInput.addEventListener("input", () => { accentInput.addEventListener("input", () => {
Glow.applyAccentColor(accentInput.value); Glow.applyAccentColor(accentInput.value);
Glow.state.settings ||= {}; Glow.state.settings ||= {};
Glow.state.settings.accent_color = accentInput.value; Glow.state.settings.accent_color = accentInput.value;
}); });
accentLabel.append(accentInput); accentLabel.append(accentInput);
accentWrap.append(accentLabel); accentWrap.append(accentLabel);
const backgroundWrap = document.createElement("div");
backgroundWrap.className = "settings-row";
const backgroundLabel = document.createElement("label");
backgroundLabel.className = "dialog-label";
backgroundLabel.textContent = "Profile background color";
const backgroundInput = document.createElement("input");
backgroundInput.type = "color";
backgroundInput.className = "accent-color-input";
backgroundInput.value =
Glow.normalizeAccentColor(Glow.state.settings?.profile_background_color) || "#121214";
backgroundInput.addEventListener("input", () => {
Glow.state.settings ||= {};
Glow.state.settings.profile_background_color = backgroundInput.value;
});
backgroundLabel.append(backgroundInput);
backgroundWrap.append(backgroundLabel);
const bannerWrap = document.createElement("div");
bannerWrap.className = "settings-row";
const bannerLabel = document.createElement("label");
bannerLabel.className = "dialog-label";
bannerLabel.textContent = "Profile banner color";
const bannerInput = document.createElement("input");
bannerInput.type = "color";
bannerInput.className = "accent-color-input";
bannerInput.value =
Glow.normalizeAccentColor(Glow.state.settings?.profile_banner_color) || "#3a3140";
bannerInput.addEventListener("input", () => {
Glow.state.settings ||= {};
Glow.state.settings.profile_banner_color = bannerInput.value;
});
bannerLabel.append(bannerInput);
bannerWrap.append(bannerLabel);
const nameColorWrap = document.createElement("div"); const nameColorWrap = document.createElement("div");
nameColorWrap.className = "settings-row"; nameColorWrap.className = "settings-row";
const nameColorLabel = document.createElement("label"); const nameColorLabel = document.createElement("label");
nameColorLabel.className = "dialog-label"; nameColorLabel.className = "dialog-label";
nameColorLabel.textContent = "Username color"; nameColorLabel.textContent = "Username color";
const nameColorInput = document.createElement("input"); const nameColorInput = document.createElement("input");
nameColorInput.type = "color"; nameColorInput.type = "color";
nameColorInput.className = "accent-color-input"; nameColorInput.className = "accent-color-input";
@ -300,13 +293,11 @@ Glow.renderSettingsView = () => {
nameColorText.type = "text"; nameColorText.type = "text";
nameColorText.maxLength = 7; nameColorText.maxLength = 7;
nameColorText.value = nameColorText.value =
Glow.normalizeNameColor(Glow.state.settings?.name_color) || Glow.normalizeNameColor(Glow.state.settings?.name_color) || "";
"";
nameColorText.placeholder = "#7dd3fc"; nameColorText.placeholder = "#7dd3fc";
const nameRow = document.createElement("div"); const nameRow = document.createElement("div");
nameRow.className = "accent-picker-row"; nameRow.className = "accent-picker-row";
const previewSwatch = document.createElement("span"); const previewSwatch = document.createElement("span");
previewSwatch.className = "accent-preview"; previewSwatch.className = "accent-preview";
previewSwatch.style.background = previewSwatch.style.background =
@ -322,7 +313,6 @@ Glow.renderSettingsView = () => {
const normalized = Glow.normalizeNameColor(value); const normalized = Glow.normalizeNameColor(value);
const finalColor = const finalColor =
normalized || Glow.roleDefaultNameColor(Glow.state.currentUser?.role); normalized || Glow.roleDefaultNameColor(Glow.state.currentUser?.role);
previewSwatch.style.background = finalColor; previewSwatch.style.background = finalColor;
return finalColor; return finalColor;
}; };
@ -354,44 +344,108 @@ Glow.renderSettingsView = () => {
nameRow.append(nameColorInput, nameColorText, previewSwatch); nameRow.append(nameColorInput, nameColorText, previewSwatch);
nameColorLabel.append(nameRow); nameColorLabel.append(nameRow);
nameColorWrap.append(nameColorLabel, help, Glow.renderNameColorPreview( nameColorWrap.append(
Glow.state.settings?.name_color nameColorLabel,
), resetNameColor); help,
Glow.renderNameColorPreview(Glow.state.settings?.name_color),
resetNameColor
);
appearance.append(aTitle, aText, themeRow, accentWrap, nameColorWrap); appearance.append(
aTitle,
aText,
themeRow,
accentWrap,
backgroundWrap,
bannerWrap,
nameColorWrap
);
const presenceSection = document.createElement("section");
presenceSection.className = "settings-section";
const pTitle = document.createElement("h2");
pTitle.textContent = "Presence & privacy";
const pText = document.createElement("p");
pText.textContent = "Control who can view your profile and last active time, and set your presence mode.";
const profileVisibility = document.createElement("input");
profileVisibility.className = "field";
profileVisibility.value = Glow.state.settings?.profile_visibility || "public";
profileVisibility.placeholder = "public | members | private";
const lastActiveVisibility = document.createElement("input");
lastActiveVisibility.className = "field";
lastActiveVisibility.value = Glow.state.settings?.last_active_visibility || "everyone";
lastActiveVisibility.placeholder = "everyone | members | nobody";
const presenceMode = document.createElement("input");
presenceMode.className = "field";
presenceMode.value = Glow.state.settings?.presence_mode || Glow.state.currentUser?.presence_mode || "online";
presenceMode.placeholder = "online | silent | focus | dnd";
profileVisibility.addEventListener("input", () => {
Glow.state.settings ||= {};
Glow.state.settings.profile_visibility = profileVisibility.value.trim().toLowerCase();
});
lastActiveVisibility.addEventListener("input", () => {
Glow.state.settings ||= {};
Glow.state.settings.last_active_visibility = lastActiveVisibility.value.trim().toLowerCase();
});
presenceMode.addEventListener("input", () => {
Glow.state.settings ||= {};
Glow.state.settings.presence_mode = presenceMode.value.trim().toLowerCase();
});
presenceSection.append(
pTitle,
pText,
(() => {
const label = document.createElement("label");
label.className = "dialog-label";
label.textContent = "Profile visibility";
label.append(profileVisibility);
return label;
})(),
(() => {
const label = document.createElement("label");
label.className = "dialog-label";
label.textContent = "Last active visibility";
label.append(lastActiveVisibility);
return label;
})(),
(() => {
const label = document.createElement("label");
label.className = "dialog-label";
label.textContent = "Presence mode";
label.append(presenceMode);
return label;
})()
);
const cssSection = document.createElement("section"); const cssSection = document.createElement("section");
cssSection.className = "settings-section"; cssSection.className = "settings-section";
const cTitle = document.createElement("h2"); const cTitle = document.createElement("h2");
cTitle.textContent = "Custom CSS"; cTitle.textContent = "Custom CSS";
const cText = document.createElement("p"); const cText = document.createElement("p");
cText.textContent = cText.textContent =
"Optional CSS applied only for your account in this browser session and saved to your settings. \n ex: *,html,body{font-size:95%;text-transform:lowercase;}"; "Optional CSS applied only for your account in this browser session and saved to your settings. \n ex: *,html,body{font-size:95%;text-transform:lowercase;}";
const cssInput = document.createElement("textarea"); const cssInput = document.createElement("textarea");
cssInput.className = "field settings-custom-css"; cssInput.className = "field settings-custom-css";
cssInput.placeholder = "/* Custom CSS */"; cssInput.placeholder = "/* Custom CSS */";
cssInput.value = Glow.state.settings?.custom_css || ""; cssInput.value = Glow.state.settings?.custom_css || "";
cssInput.addEventListener("input", () => { cssInput.addEventListener("input", () => {
Glow.applyCustomCss(cssInput.value); Glow.applyCustomCss(cssInput.value);
Glow.state.settings ||= {}; Glow.state.settings ||= {};
Glow.state.settings.custom_css = cssInput.value; Glow.state.settings.custom_css = cssInput.value;
}); });
cssSection.append(cTitle, cText, cssInput); cssSection.append(cTitle, cText, cssInput);
const accountSection = document.createElement("section"); const accountSection = document.createElement("section");
accountSection.className = "settings-section"; accountSection.className = "settings-section";
const accountTitle = document.createElement("h2"); const accountTitle = document.createElement("h2");
accountTitle.textContent = "Account"; accountTitle.textContent = "Account";
const accountText = document.createElement("p"); const accountText = document.createElement("p");
accountText.textContent = "Update your public profile and application preferences."; accountText.textContent = "Update your public profile and application preferences.";
const actions = document.createElement("div"); const actions = document.createElement("div");
actions.className = "settings-actions"; actions.className = "settings-actions";
@ -420,11 +474,23 @@ Glow.renderSettingsView = () => {
theme_mode: theme_mode:
Glow.state.settings?.theme_mode || Glow.state.settings?.theme_mode ||
localStorage.getItem("glow_theme_mode") || localStorage.getItem("glow_theme_mode") ||
"dark" "dark",
profile_background_color:
Glow.normalizeAccentColor(Glow.state.settings?.profile_background_color) || null,
profile_banner_color:
Glow.normalizeAccentColor(Glow.state.settings?.profile_banner_color) || null,
profile_visibility:
Glow.state.settings?.profile_visibility || "public",
last_active_visibility:
Glow.state.settings?.last_active_visibility || "everyone",
presence_mode:
Glow.state.settings?.presence_mode || "online"
}; };
const data = await Glow.apiUpdateMySettings(payload); const data = await Glow.apiUpdateMySettings(payload);
Glow.state.settings = data.settings || payload; Glow.state.settings = data.settings || payload;
if (Glow.state.currentUser) {
Glow.state.currentUser.presence_mode = Glow.state.settings.presence_mode || "online";
}
Glow.showToast("Settings saved."); Glow.showToast("Settings saved.");
Glow.renderSettingsView(); Glow.renderSettingsView();
} catch (error) { } catch (error) {
@ -446,6 +512,6 @@ Glow.renderSettingsView = () => {
actions.append(editProfile, saveSettings, resetAccent); actions.append(editProfile, saveSettings, resetAccent);
accountSection.append(accountTitle, accountText, actions); accountSection.append(accountTitle, accountText, actions);
card.append(appearance, cssSection, accountSection); card.append(appearance, presenceSection, cssSection, accountSection);
box.append(card); box.append(card);
}; };