Update static/js/08-settings.js

This commit is contained in:
dev 2026-10-31 09:17:00 +00:00
parent 6aeac4659c
commit 0a31e32f1e

View File

@ -20,21 +20,211 @@ Glow.applyCustomCss = cssText => {
style.textContent = typeof cssText === "string" ? cssText : ""; style.textContent = typeof cssText === "string" ? cssText : "";
}; };
Glow.cloneSettingsState = source => {
const value = source && typeof source === "object" ? source : {};
return {
accent_color:
Glow.normalizeAccentColor(value.accent_color || Glow.state.accentColor) ||
Glow.DEFAULT_ACCENT_COLOR,
name_color:
Glow.normalizeNameColor(value.name_color) || null,
custom_css:
typeof value.custom_css === "string" ? value.custom_css : "",
theme_mode:
value.theme_mode === "light" ? "light" : "dark",
profile_background_color:
Glow.normalizeAccentColor(value.profile_background_color) || "#121214",
profile_banner_color:
Glow.normalizeAccentColor(value.profile_banner_color) || "#3a3140",
profile_visibility:
["public", "members", "private"].includes(
String(value.profile_visibility || "").toLowerCase()
)
? String(value.profile_visibility).toLowerCase()
: "public",
last_active_visibility:
["everyone", "members", "nobody"].includes(
String(value.last_active_visibility || "").toLowerCase()
)
? String(value.last_active_visibility).toLowerCase()
: "everyone",
presence_mode:
["online", "silent", "focus", "dnd"].includes(
String(value.presence_mode || "").toLowerCase()
)
? String(value.presence_mode).toLowerCase()
: "online",
dm_layout_mode:
value.dm_layout_mode === "compact"
? "compact"
: "cards",
show_presence_badge:
typeof value.show_presence_badge === "boolean"
? value.show_presence_badge
: Glow.state.profilePresenceBadgeVisible !== false
};
};
Glow.initializeSettingsDraft = settings => {
const saved = Glow.cloneSettingsState(settings);
Glow.state.settings = saved;
Glow.state.settingsSaved = saved;
Glow.state.settingsDraft = Glow.cloneSettingsState(saved);
Glow.clearSettingsDirty();
};
Glow.previewSettingsDraft = () => {
const draft = Glow.state.settingsDraft || Glow.cloneSettingsState({});
Glow.applyAccentColor(draft.accent_color);
Glow.applyThemeMode(draft.theme_mode);
Glow.applyCustomCss(draft.custom_css || "");
Glow.saveDmLayoutPreference(draft.dm_layout_mode || "cards");
Glow.state.profilePresenceBadgeVisible = draft.show_presence_badge !== false;
};
Glow.updateSettingsDraft = patch => {
const current = Glow.state.settingsDraft || Glow.cloneSettingsState({});
Glow.state.settingsDraft = {
...current,
...patch
};
Glow.previewSettingsDraft();
Glow.markSettingsDirty();
};
Glow.syncSettingsSavePanel = () => {
const panel = Glow.els.settingsSavePanel;
if (!panel) return;
const shouldShow =
Glow.state.activeView === "settings" &&
Glow.isSettingsDirty();
panel.classList.toggle("hidden", !shouldShow);
if (Glow.els.settingsUnsavedIndicator) {
Glow.els.settingsUnsavedIndicator.textContent = shouldShow
? "Unsaved changes"
: "";
}
};
Glow.saveSettingsDraft = async () => {
const draft = Glow.state.settingsDraft || Glow.cloneSettingsState({});
const payload = {
accent_color:
Glow.normalizeAccentColor(draft.accent_color) ||
Glow.DEFAULT_ACCENT_COLOR,
name_color:
Glow.normalizeNameColor(draft.name_color) || null,
custom_css: draft.custom_css || "",
theme_mode: draft.theme_mode === "light" ? "light" : "dark",
profile_background_color:
Glow.normalizeAccentColor(draft.profile_background_color) || null,
profile_banner_color:
Glow.normalizeAccentColor(draft.profile_banner_color) || null,
profile_visibility:
["public", "members", "private"].includes(draft.profile_visibility)
? draft.profile_visibility
: "public",
last_active_visibility:
["everyone", "members", "nobody"].includes(draft.last_active_visibility)
? draft.last_active_visibility
: "everyone",
presence_mode:
["online", "silent", "focus", "dnd"].includes(draft.presence_mode)
? draft.presence_mode
: "online",
dm_layout_mode:
draft.dm_layout_mode === "compact" ? "compact" : "cards",
show_presence_badge: draft.show_presence_badge !== false
};
const data = await Glow.apiUpdateMySettings(payload);
const merged = {
...Glow.cloneSettingsState(draft),
...(data.settings || {})
};
Glow.state.settings = merged;
Glow.state.settingsSaved = Glow.cloneSettingsState(merged);
Glow.state.settingsDraft = Glow.cloneSettingsState(merged);
Glow.previewSettingsDraft();
Glow.clearSettingsDirty();
if (Glow.state.currentUser) {
Glow.state.currentUser.presence_mode =
merged.presence_mode || Glow.state.currentUser.presence_mode || "online";
}
Glow.showToast("Settings saved.");
Glow.renderSettingsView();
};
Glow.discardSettingsDraft = () => {
const saved = Glow.state.settingsSaved || Glow.cloneSettingsState({});
Glow.state.settingsDraft = Glow.cloneSettingsState(saved);
Glow.previewSettingsDraft();
Glow.clearSettingsDirty();
if (Glow.state.activeView === "settings") {
Glow.renderSettingsView();
}
};
Glow.closeSettingsViewRequested = async () => {
if (!Glow.isSettingsDirty()) {
await Glow.openFeed?.();
return true;
}
const values = await Glow.openDialog({
title: "Unsaved settings changes",
body: "You have unsaved settings changes. Save them before leaving?",
confirmText: "Save and leave",
cancelText: "Stay"
});
if (values === null) {
return false;
}
try {
await Glow.saveSettingsDraft();
await Glow.openFeed?.();
return true;
} catch (error) {
Glow.showToast(error.message || "Could not save settings.", "error");
return false;
}
};
Glow.loadMySettings = async () => { Glow.loadMySettings = async () => {
try { try {
const data = await Glow.apiGetMySettings(); const data = await Glow.apiGetMySettings();
Glow.state.settings = data.settings || {}; const base = Glow.cloneSettingsState({
const accent = Glow.state.settings.accent_color || Glow.state.accentColor; ...(data.settings || {}),
Glow.applyAccentColor(accent); dm_layout_mode:
const mode = data.settings?.dm_layout_mode || Glow.loadDmLayoutPreference(),
Glow.state.settings.theme_mode || show_presence_badge:
localStorage.getItem("glow_theme_mode") || typeof data.settings?.show_presence_badge === "boolean"
"dark"; ? data.settings.show_presence_badge
Glow.applyThemeMode(mode); : Glow.state.profilePresenceBadgeVisible !== false
Glow.applyCustomCss(Glow.state.settings.custom_css || ""); });
Glow.initializeSettingsDraft(base);
Glow.previewSettingsDraft();
} catch (_) { } catch (_) {
const mode = localStorage.getItem("glow_theme_mode") || "dark"; const fallback = Glow.cloneSettingsState({
Glow.applyThemeMode(mode); theme_mode: localStorage.getItem("glow_theme_mode") || "dark",
accent_color: Glow.state.accentColor,
dm_layout_mode: Glow.loadDmLayoutPreference(),
show_presence_badge: Glow.state.profilePresenceBadgeVisible !== false,
profile_visibility: "public",
last_active_visibility: "everyone",
presence_mode: "online"
});
Glow.initializeSettingsDraft(fallback);
Glow.previewSettingsDraft();
} }
}; };
@ -169,10 +359,16 @@ 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.settingsSaved && Glow.state.currentUser) {
await Glow.loadMySettings(); await Glow.loadMySettings();
} else if (!Glow.state.settingsDraft) {
Glow.initializeSettingsDraft(
Glow.state.settingsSaved || Glow.cloneSettingsState({})
);
Glow.previewSettingsDraft();
} }
Glow.renderSettingsView(); Glow.renderSettingsView();
Glow.syncSettingsSavePanel();
}; };
Glow.renderNameColorPreview = value => { Glow.renderNameColorPreview = value => {
@ -195,13 +391,205 @@ Glow.renderNameColorPreview = value => {
return wrap; return wrap;
}; };
Glow.renderChoicePills = ({
label,
value,
options,
onChange
}) => {
const wrap = document.createElement("div");
wrap.className = "settings-row";
const title = document.createElement("div");
title.className = "dialog-label";
title.textContent = label;
const row = document.createElement("div");
row.className = "settings-theme-row";
for (const option of options) {
const button = document.createElement("button");
button.className = `theme-pill${value === option.value ? " active" : ""}`;
button.type = "button";
button.textContent = option.label;
button.addEventListener("click", () => onChange(option.value));
row.append(button);
}
wrap.append(title, row);
return wrap;
};
Glow.renderSettingsView = () => { Glow.renderSettingsView = () => {
const box = Glow.els.profileBox; const box = Glow.els.profileBox;
box.replaceChildren(); box.replaceChildren();
const draft = Glow.state.settingsDraft || Glow.cloneSettingsState({});
const card = document.createElement("div"); const card = document.createElement("div");
card.className = "settings-card"; card.className = "settings-card";
const identity = document.createElement("section");
identity.className = "settings-section";
const iTitle = document.createElement("h2");
iTitle.textContent = "Identity";
const iText = document.createElement("p");
iText.textContent = "Adjust how your name and profile presence appear across the app.";
const nameColorWrap = document.createElement("div");
nameColorWrap.className = "settings-row";
const nameColorLabel = document.createElement("label");
nameColorLabel.className = "dialog-label";
nameColorLabel.textContent = "Username color";
const nameColorInput = document.createElement("input");
nameColorInput.type = "color";
nameColorInput.className = "accent-color-input";
nameColorInput.value =
Glow.normalizeNameColor(draft.name_color) ||
Glow.roleDefaultNameColor(Glow.state.currentUser?.role);
const nameColorText = document.createElement("input");
nameColorText.className = "field accent-hex-input";
nameColorText.type = "text";
nameColorText.maxLength = 7;
nameColorText.value =
Glow.normalizeNameColor(draft.name_color) || "";
nameColorText.placeholder = "#7dd3fc";
const nameRow = document.createElement("div");
nameRow.className = "accent-picker-row";
const previewSwatch = document.createElement("span");
previewSwatch.className = "accent-preview";
previewSwatch.style.background =
Glow.normalizeNameColor(draft.name_color) ||
Glow.roleDefaultNameColor(Glow.state.currentUser?.role);
nameColorInput.addEventListener("input", () => {
nameColorText.value = nameColorInput.value;
Glow.updateSettingsDraft({
name_color: nameColorInput.value
});
});
nameColorText.addEventListener("input", () => {
const normalized = Glow.normalizeNameColor(nameColorText.value.trim());
Glow.updateSettingsDraft({
name_color: normalized || null
});
});
const resetNameColor = document.createElement("button");
resetNameColor.className = "button secondary";
resetNameColor.type = "button";
resetNameColor.textContent = "Use role default";
resetNameColor.addEventListener("click", () => {
Glow.updateSettingsDraft({
name_color: null
});
Glow.renderSettingsView();
});
nameRow.append(nameColorInput, nameColorText, previewSwatch);
nameColorLabel.append(nameRow);
nameColorWrap.append(
nameColorLabel,
Glow.renderNameColorPreview(draft.name_color),
resetNameColor
);
const dmLayout = Glow.renderChoicePills({
label: "DM layout",
value: draft.dm_layout_mode || "cards",
options: [
{ value: "cards", label: "Cards" },
{ value: "compact", label: "Compact" }
],
onChange: value => {
Glow.updateSettingsDraft({ dm_layout_mode: value });
Glow.loadDms?.().catch(() => {});
Glow.renderSettingsView();
}
});
const presenceBadge = Glow.renderChoicePills({
label: "Profile presence badge",
value: draft.show_presence_badge === false ? "hidden" : "shown",
options: [
{ value: "shown", label: "Shown" },
{ value: "hidden", label: "Hidden" }
],
onChange: value => {
Glow.updateSettingsDraft({
show_presence_badge: value !== "hidden"
});
Glow.renderSettingsView();
}
});
const profileVisibility = Glow.renderChoicePills({
label: "Profile visibility",
value: draft.profile_visibility || "public",
options: [
{ value: "public", label: "Public" },
{ value: "members", label: "Members" },
{ value: "private", label: "Private" }
],
onChange: value => {
Glow.updateSettingsDraft({
profile_visibility: value
});
Glow.renderSettingsView();
}
});
const lastActiveVisibility = Glow.renderChoicePills({
label: "Last active visibility",
value: draft.last_active_visibility || "everyone",
options: [
{ value: "everyone", label: "Everyone" },
{ value: "members", label: "Members" },
{ value: "nobody", label: "Nobody" }
],
onChange: value => {
Glow.updateSettingsDraft({
last_active_visibility: value
});
Glow.renderSettingsView();
}
});
const presenceMode = Glow.renderChoicePills({
label: "Presence mode",
value: draft.presence_mode || "online",
options: [
{ value: "online", label: "Online" },
{ value: "silent", label: "Silent" },
{ value: "focus", label: "Focus" },
{ value: "dnd", label: "Dnd" }
],
onChange: value => {
Glow.updateSettingsDraft({
presence_mode: value
});
Glow.renderSettingsView();
}
});
identity.append(
iTitle,
iText,
nameColorWrap,
dmLayout,
presenceBadge,
profileVisibility,
lastActiveVisibility,
presenceMode
);
const appearance = document.createElement("section"); const appearance = document.createElement("section");
appearance.className = "settings-section"; appearance.className = "settings-section";
@ -209,37 +597,28 @@ Glow.renderSettingsView = () => {
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, profile background, and banner color."; aText.textContent = "Set theme and accent color.";
const themeRow = document.createElement("div"); const themeRow = document.createElement("div");
themeRow.className = "settings-theme-row"; themeRow.className = "settings-theme-row";
const currentTheme =
Glow.state.settings?.theme_mode ||
localStorage.getItem("glow_theme_mode") ||
"dark";
const darkButton = document.createElement("button"); const darkButton = document.createElement("button");
darkButton.className = `theme-pill${currentTheme !== "light" ? " active" : ""}`; darkButton.className = `theme-pill${draft.theme_mode !== "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${draft.theme_mode === "light" ? " active" : ""}`;
lightButton.type = "button"; lightButton.type = "button";
lightButton.textContent = "Light"; lightButton.textContent = "Light";
darkButton.addEventListener("click", () => { darkButton.addEventListener("click", () => {
Glow.applyThemeMode("dark"); Glow.updateSettingsDraft({ theme_mode: "dark" });
Glow.state.settings ||= {};
Glow.state.settings.theme_mode = "dark";
Glow.renderSettingsView(); Glow.renderSettingsView();
}); });
lightButton.addEventListener("click", () => { lightButton.addEventListener("click", () => {
Glow.applyThemeMode("light"); Glow.updateSettingsDraft({ theme_mode: "light" });
Glow.state.settings ||= {};
Glow.state.settings.theme_mode = "light";
Glow.renderSettingsView(); Glow.renderSettingsView();
}); });
@ -256,19 +635,29 @@ Glow.renderSettingsView = () => {
accentInput.type = "color"; accentInput.type = "color";
accentInput.className = "accent-color-input"; accentInput.className = "accent-color-input";
accentInput.value = accentInput.value =
Glow.normalizeAccentColor( Glow.normalizeAccentColor(draft.accent_color) ||
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.updateSettingsDraft({
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);
appearance.append(aTitle, aText, themeRow, accentWrap);
const profileVisuals = document.createElement("section");
profileVisuals.className = "settings-section";
const pvTitle = document.createElement("h2");
pvTitle.textContent = "Profile visuals";
const pvText = document.createElement("p");
pvText.textContent = "Adjust background and banner colors used in your profile view.";
const backgroundWrap = document.createElement("div"); const backgroundWrap = document.createElement("div");
backgroundWrap.className = "settings-row"; backgroundWrap.className = "settings-row";
@ -280,11 +669,12 @@ Glow.renderSettingsView = () => {
backgroundInput.type = "color"; backgroundInput.type = "color";
backgroundInput.className = "accent-color-input"; backgroundInput.className = "accent-color-input";
backgroundInput.value = backgroundInput.value =
Glow.normalizeAccentColor(Glow.state.settings?.profile_background_color) || "#121214"; Glow.normalizeAccentColor(draft.profile_background_color) || "#121214";
backgroundInput.addEventListener("input", () => { backgroundInput.addEventListener("input", () => {
Glow.state.settings ||= {}; Glow.updateSettingsDraft({
Glow.state.settings.profile_background_color = backgroundInput.value; profile_background_color: backgroundInput.value
});
}); });
backgroundLabel.append(backgroundInput); backgroundLabel.append(backgroundInput);
@ -301,103 +691,17 @@ Glow.renderSettingsView = () => {
bannerInput.type = "color"; bannerInput.type = "color";
bannerInput.className = "accent-color-input"; bannerInput.className = "accent-color-input";
bannerInput.value = bannerInput.value =
Glow.normalizeAccentColor(Glow.state.settings?.profile_banner_color) || "#3a3140"; Glow.normalizeAccentColor(draft.profile_banner_color) || "#3a3140";
bannerInput.addEventListener("input", () => { bannerInput.addEventListener("input", () => {
Glow.state.settings ||= {}; Glow.updateSettingsDraft({
Glow.state.settings.profile_banner_color = bannerInput.value; profile_banner_color: bannerInput.value
});
}); });
bannerLabel.append(bannerInput); bannerLabel.append(bannerInput);
bannerWrap.append(bannerLabel); bannerWrap.append(backgroundLabel, bannerLabel);
profileVisuals.append(pvTitle, pvText, backgroundWrap, bannerLabel);
const nameColorWrap = document.createElement("div");
nameColorWrap.className = "settings-row";
const nameColorLabel = document.createElement("label");
nameColorLabel.className = "dialog-label";
nameColorLabel.textContent = "Username color";
const nameColorInput = document.createElement("input");
nameColorInput.type = "color";
nameColorInput.className = "accent-color-input";
nameColorInput.value =
Glow.normalizeNameColor(Glow.state.settings?.name_color) ||
Glow.roleDefaultNameColor(Glow.state.currentUser?.role);
const nameColorText = document.createElement("input");
nameColorText.className = "field accent-hex-input";
nameColorText.type = "text";
nameColorText.maxLength = 7;
nameColorText.value =
Glow.normalizeNameColor(Glow.state.settings?.name_color) || "";
nameColorText.placeholder = "#7dd3fc";
const nameRow = document.createElement("div");
nameRow.className = "accent-picker-row";
const previewSwatch = document.createElement("span");
previewSwatch.className = "accent-preview";
previewSwatch.style.background =
Glow.normalizeNameColor(Glow.state.settings?.name_color) ||
Glow.roleDefaultNameColor(Glow.state.currentUser?.role);
const help = document.createElement("div");
help.className = "header-subtitle";
help.textContent =
`Leave empty to use your role default (${Glow.roleDefaultNameColor(Glow.state.currentUser?.role)}).`;
const updatePreview = value => {
const normalized = Glow.normalizeNameColor(value);
const finalColor =
normalized || Glow.roleDefaultNameColor(Glow.state.currentUser?.role);
previewSwatch.style.background = finalColor;
return finalColor;
};
nameColorInput.addEventListener("input", () => {
nameColorText.value = nameColorInput.value;
Glow.state.settings ||= {};
Glow.state.settings.name_color = nameColorInput.value;
updatePreview(nameColorInput.value);
Glow.renderSettingsView();
});
nameColorText.addEventListener("input", () => {
const normalized = Glow.normalizeNameColor(nameColorText.value.trim());
Glow.state.settings ||= {};
Glow.state.settings.name_color = normalized || null;
updatePreview(nameColorText.value.trim());
});
const resetNameColor = document.createElement("button");
resetNameColor.className = "button secondary";
resetNameColor.type = "button";
resetNameColor.textContent = "Use role default";
resetNameColor.addEventListener("click", () => {
Glow.state.settings ||= {};
Glow.state.settings.name_color = null;
Glow.renderSettingsView();
});
nameRow.append(nameColorInput, nameColorText, previewSwatch);
nameColorLabel.append(nameRow);
nameColorWrap.append(
nameColorLabel,
help,
Glow.renderNameColorPreview(Glow.state.settings?.name_color),
resetNameColor
);
appearance.append(
aTitle,
aText,
themeRow,
accentWrap,
backgroundWrap,
bannerWrap,
nameColorWrap
);
const cssSection = document.createElement("section"); const cssSection = document.createElement("section");
cssSection.className = "settings-section"; cssSection.className = "settings-section";
@ -407,16 +711,16 @@ Glow.renderSettingsView = () => {
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 locally for preview and saved to your account when supported.";
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 = draft.custom_css || "";
cssInput.addEventListener("input", () => { cssInput.addEventListener("input", () => {
Glow.applyCustomCss(cssInput.value); Glow.updateSettingsDraft({
Glow.state.settings ||= {}; custom_css: cssInput.value
Glow.state.settings.custom_css = cssInput.value; });
}); });
cssSection.append(cTitle, cText, cssInput); cssSection.append(cTitle, cText, cssInput);
@ -428,7 +732,7 @@ Glow.renderSettingsView = () => {
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 = "Open your profile editor or reset visual customizations.";
const actions = document.createElement("div"); const actions = document.createElement("div");
actions.className = "settings-actions"; actions.className = "settings-actions";
@ -441,64 +745,57 @@ Glow.renderSettingsView = () => {
Glow.openMyProfile?.(true); Glow.openMyProfile?.(true);
}); });
const saveSettings = document.createElement("button");
saveSettings.className = "button";
saveSettings.type = "button";
saveSettings.textContent = "Save settings";
saveSettings.addEventListener("click", async () => {
try {
const payload = {
accent_color:
Glow.normalizeAccentColor(
Glow.state.settings?.accent_color || Glow.state.accentColor
) || Glow.DEFAULT_ACCENT_COLOR,
name_color:
Glow.normalizeNameColor(Glow.state.settings?.name_color) || null,
custom_css: Glow.state.settings?.custom_css || "",
theme_mode:
Glow.state.settings?.theme_mode ||
localStorage.getItem("glow_theme_mode") ||
"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);
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.renderSettingsView();
} catch (error) {
Glow.showToast(error.message || "Could not save settings.", "error");
}
});
const resetAccent = document.createElement("button"); const resetAccent = document.createElement("button");
resetAccent.className = "button secondary"; resetAccent.className = "button secondary";
resetAccent.type = "button"; resetAccent.type = "button";
resetAccent.textContent = "Reset accent"; resetAccent.textContent = "Reset accent";
resetAccent.addEventListener("click", () => { resetAccent.addEventListener("click", () => {
Glow.resetAccentColor(); Glow.updateSettingsDraft({
Glow.state.settings ||= {}; accent_color: Glow.DEFAULT_ACCENT_COLOR
Glow.state.settings.accent_color = Glow.DEFAULT_ACCENT_COLOR; });
Glow.renderSettingsView(); Glow.renderSettingsView();
}); });
actions.append(editProfile, saveSettings, resetAccent); const discard = document.createElement("button");
discard.className = "button secondary";
discard.type = "button";
discard.textContent = "Discard changes";
discard.addEventListener("click", () => {
Glow.discardSettingsDraft();
});
actions.append(editProfile, resetAccent, discard);
accountSection.append(accountTitle, accountText, actions); accountSection.append(accountTitle, accountText, actions);
card.append(appearance, cssSection, accountSection); card.append(identity, appearance, profileVisuals, cssSection, accountSection);
box.append(card); box.append(card);
Glow.syncSettingsSavePanel();
}; };
Glow.els.settingsSaveButton?.addEventListener("click", async () => {
try {
await Glow.saveSettingsDraft();
} catch (error) {
Glow.showToast(error.message || "Could not save settings.", "error");
}
});
Glow.els.settingsCloseButton?.addEventListener("click", async () => {
if (!Glow.isSettingsDirty()) {
await Glow.openFeed?.();
return;
}
const values = await Glow.openDialog({
title: "Leave settings?",
body: "You have unsaved changes.",
confirmText: "Discard changes",
cancelText: "Stay",
danger: true
});
if (!values) return;
Glow.discardSettingsDraft();
await Glow.openFeed?.();
});