Update static/js/08-settings.js
This commit is contained in:
parent
6aeac4659c
commit
0a31e32f1e
@ -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?.();
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user