diff --git a/static/js/08-settings.js b/static/js/08-settings.js index ad0d897..64edd0d 100644 --- a/static/js/08-settings.js +++ b/static/js/08-settings.js @@ -20,21 +20,211 @@ Glow.applyCustomCss = 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 () => { try { const data = await Glow.apiGetMySettings(); - Glow.state.settings = data.settings || {}; - const accent = Glow.state.settings.accent_color || Glow.state.accentColor; - Glow.applyAccentColor(accent); - const mode = - Glow.state.settings.theme_mode || - localStorage.getItem("glow_theme_mode") || - "dark"; - Glow.applyThemeMode(mode); - Glow.applyCustomCss(Glow.state.settings.custom_css || ""); + const base = Glow.cloneSettingsState({ + ...(data.settings || {}), + dm_layout_mode: + data.settings?.dm_layout_mode || Glow.loadDmLayoutPreference(), + show_presence_badge: + typeof data.settings?.show_presence_badge === "boolean" + ? data.settings.show_presence_badge + : Glow.state.profilePresenceBadgeVisible !== false + }); + Glow.initializeSettingsDraft(base); + Glow.previewSettingsDraft(); } catch (_) { - const mode = localStorage.getItem("glow_theme_mode") || "dark"; - Glow.applyThemeMode(mode); + const fallback = Glow.cloneSettingsState({ + 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.state.activeView = "settings"; Glow.updateViewUI(); - if (!Glow.state.settings && Glow.state.currentUser) { + if (!Glow.state.settingsSaved && Glow.state.currentUser) { await Glow.loadMySettings(); + } else if (!Glow.state.settingsDraft) { + Glow.initializeSettingsDraft( + Glow.state.settingsSaved || Glow.cloneSettingsState({}) + ); + Glow.previewSettingsDraft(); } Glow.renderSettingsView(); + Glow.syncSettingsSavePanel(); }; Glow.renderNameColorPreview = value => { @@ -195,13 +391,205 @@ Glow.renderNameColorPreview = value => { 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 = () => { const box = Glow.els.profileBox; box.replaceChildren(); + const draft = Glow.state.settingsDraft || Glow.cloneSettingsState({}); const card = document.createElement("div"); 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"); appearance.className = "settings-section"; @@ -209,37 +597,28 @@ Glow.renderSettingsView = () => { aTitle.textContent = "Appearance"; 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"); themeRow.className = "settings-theme-row"; - const currentTheme = - Glow.state.settings?.theme_mode || - localStorage.getItem("glow_theme_mode") || - "dark"; - 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.textContent = "Dark"; 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.textContent = "Light"; darkButton.addEventListener("click", () => { - Glow.applyThemeMode("dark"); - Glow.state.settings ||= {}; - Glow.state.settings.theme_mode = "dark"; + Glow.updateSettingsDraft({ theme_mode: "dark" }); Glow.renderSettingsView(); }); lightButton.addEventListener("click", () => { - Glow.applyThemeMode("light"); - Glow.state.settings ||= {}; - Glow.state.settings.theme_mode = "light"; + Glow.updateSettingsDraft({ theme_mode: "light" }); Glow.renderSettingsView(); }); @@ -256,19 +635,29 @@ Glow.renderSettingsView = () => { accentInput.type = "color"; accentInput.className = "accent-color-input"; accentInput.value = - Glow.normalizeAccentColor( - Glow.state.settings?.accent_color || Glow.state.accentColor - ) || Glow.DEFAULT_ACCENT_COLOR; + Glow.normalizeAccentColor(draft.accent_color) || + Glow.DEFAULT_ACCENT_COLOR; accentInput.addEventListener("input", () => { - Glow.applyAccentColor(accentInput.value); - Glow.state.settings ||= {}; - Glow.state.settings.accent_color = accentInput.value; + Glow.updateSettingsDraft({ + accent_color: accentInput.value + }); }); accentLabel.append(accentInput); 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"); backgroundWrap.className = "settings-row"; @@ -280,11 +669,12 @@ Glow.renderSettingsView = () => { backgroundInput.type = "color"; backgroundInput.className = "accent-color-input"; backgroundInput.value = - Glow.normalizeAccentColor(Glow.state.settings?.profile_background_color) || "#121214"; + Glow.normalizeAccentColor(draft.profile_background_color) || "#121214"; backgroundInput.addEventListener("input", () => { - Glow.state.settings ||= {}; - Glow.state.settings.profile_background_color = backgroundInput.value; + Glow.updateSettingsDraft({ + profile_background_color: backgroundInput.value + }); }); backgroundLabel.append(backgroundInput); @@ -301,103 +691,17 @@ Glow.renderSettingsView = () => { bannerInput.type = "color"; bannerInput.className = "accent-color-input"; bannerInput.value = - Glow.normalizeAccentColor(Glow.state.settings?.profile_banner_color) || "#3a3140"; + Glow.normalizeAccentColor(draft.profile_banner_color) || "#3a3140"; bannerInput.addEventListener("input", () => { - Glow.state.settings ||= {}; - Glow.state.settings.profile_banner_color = bannerInput.value; + Glow.updateSettingsDraft({ + profile_banner_color: bannerInput.value + }); }); bannerLabel.append(bannerInput); - bannerWrap.append(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 - ); + bannerWrap.append(backgroundLabel, bannerLabel); + profileVisuals.append(pvTitle, pvText, backgroundWrap, bannerLabel); const cssSection = document.createElement("section"); cssSection.className = "settings-section"; @@ -407,16 +711,16 @@ Glow.renderSettingsView = () => { const cText = document.createElement("p"); 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"); cssInput.className = "field settings-custom-css"; cssInput.placeholder = "/* Custom CSS */"; - cssInput.value = Glow.state.settings?.custom_css || ""; + cssInput.value = draft.custom_css || ""; cssInput.addEventListener("input", () => { - Glow.applyCustomCss(cssInput.value); - Glow.state.settings ||= {}; - Glow.state.settings.custom_css = cssInput.value; + Glow.updateSettingsDraft({ + custom_css: cssInput.value + }); }); cssSection.append(cTitle, cText, cssInput); @@ -428,7 +732,7 @@ Glow.renderSettingsView = () => { accountTitle.textContent = "Account"; 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"); actions.className = "settings-actions"; @@ -441,64 +745,57 @@ Glow.renderSettingsView = () => { 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"); resetAccent.className = "button secondary"; resetAccent.type = "button"; resetAccent.textContent = "Reset accent"; resetAccent.addEventListener("click", () => { - Glow.resetAccentColor(); - Glow.state.settings ||= {}; - Glow.state.settings.accent_color = Glow.DEFAULT_ACCENT_COLOR; + Glow.updateSettingsDraft({ + accent_color: Glow.DEFAULT_ACCENT_COLOR + }); 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); - card.append(appearance, cssSection, accountSection); + card.append(identity, appearance, profileVisuals, cssSection, accountSection); box.append(card); -}; \ No newline at end of file + 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?.(); +}); \ No newline at end of file