feat: add My Account submenu with dark mode + encrypted backup toggles
Reorganize user dropdown into expandable "My Account" submenu containing account actions (Add Email, Add Device, Recovery) plus Dark Mode and Encrypted Backup toggle switches. Move theme toggle from toolbar into account settings, default to dark mode. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a402caacd8
commit
5c21b64a99
|
|
@ -412,12 +412,31 @@ export class RStackIdentity extends HTMLElement {
|
|||
<div class="dropdown-header">${displayName}</div>
|
||||
${notifsHTML}
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item submenu-toggle" data-action="toggle-account">
|
||||
👤 My Account <span class="submenu-arrow">▸</span>
|
||||
</button>
|
||||
<div class="submenu" id="account-submenu">
|
||||
<button class="dropdown-item submenu-item" data-action="add-email">✉️ Add Email</button>
|
||||
<button class="dropdown-item submenu-item" data-action="add-device">📱 Add Second Device</button>
|
||||
<button class="dropdown-item submenu-item" data-action="add-recovery">🛡️ Add Social Recovery</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="dropdown-item submenu-item toggle-row">
|
||||
🌙 Dark Mode
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="theme-toggle" />
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="dropdown-item submenu-item toggle-row">
|
||||
🔒 Encrypted Backup
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="backup-toggle" />
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button class="dropdown-item" data-action="my-spaces">🌐 My Spaces</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item" data-action="add-email">✉️ Add Email</button>
|
||||
<button class="dropdown-item" data-action="add-device">📱 Add Second Device</button>
|
||||
<button class="dropdown-item" data-action="add-recovery">🛡️ Add Social Recovery</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button class="dropdown-item dropdown-item--danger" data-action="signout">🚪 Sign Out</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -468,6 +487,13 @@ export class RStackIdentity extends HTMLElement {
|
|||
el.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const action = (el as HTMLElement).dataset.action;
|
||||
if (action === "toggle-account") {
|
||||
const submenu = this.#shadow.getElementById("account-submenu")!;
|
||||
const arrow = (el as HTMLElement).querySelector(".submenu-arrow")!;
|
||||
submenu.classList.toggle("open");
|
||||
arrow.textContent = submenu.classList.contains("open") ? "▾" : "▸";
|
||||
return;
|
||||
}
|
||||
dropdown.classList.remove("open");
|
||||
if (action === "signout") {
|
||||
clearSession();
|
||||
|
|
@ -486,6 +512,34 @@ export class RStackIdentity extends HTMLElement {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Theme toggle
|
||||
const themeToggle = this.#shadow.getElementById("theme-toggle") as HTMLInputElement;
|
||||
if (themeToggle) {
|
||||
const currentTheme = localStorage.getItem("canvas-theme") || "dark";
|
||||
themeToggle.checked = currentTheme === "dark";
|
||||
themeToggle.addEventListener("change", (e) => {
|
||||
e.stopPropagation();
|
||||
const newTheme = themeToggle.checked ? "dark" : "light";
|
||||
localStorage.setItem("canvas-theme", newTheme);
|
||||
document.body.setAttribute("data-theme", newTheme);
|
||||
document.querySelectorAll(".rstack-header, .rstack-tab-row").forEach(el => el.setAttribute("data-theme", newTheme));
|
||||
this.dispatchEvent(new CustomEvent("theme-change", { bubbles: true, composed: true, detail: { theme: newTheme } }));
|
||||
this.#render();
|
||||
});
|
||||
}
|
||||
|
||||
// Backup toggle
|
||||
const backupToggle = this.#shadow.getElementById("backup-toggle") as HTMLInputElement;
|
||||
if (backupToggle) {
|
||||
backupToggle.checked = localStorage.getItem("encryptid_backup_enabled") === "true";
|
||||
backupToggle.addEventListener("change", (e) => {
|
||||
e.stopPropagation();
|
||||
const enabled = backupToggle.checked;
|
||||
localStorage.setItem("encryptid_backup_enabled", enabled ? "true" : "false");
|
||||
this.dispatchEvent(new CustomEvent("backup-toggle", { bubbles: true, composed: true, detail: { enabled } }));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.#shadow.innerHTML = `
|
||||
<style>${STYLES}</style>
|
||||
|
|
@ -1268,6 +1322,45 @@ const STYLES = `
|
|||
.notif-btn--approve:hover:not(:disabled) { opacity: 0.85; }
|
||||
.notif-btn--deny { background: rgba(239,68,68,0.15); color: #ef4444; }
|
||||
.notif-btn--deny:hover:not(:disabled) { background: rgba(239,68,68,0.25); }
|
||||
|
||||
/* Submenu accordion */
|
||||
.submenu {
|
||||
max-height: 0; overflow: hidden;
|
||||
transition: max-height 0.2s ease-out;
|
||||
}
|
||||
.submenu.open { max-height: 300px; }
|
||||
.submenu-item { padding-left: 32px !important; font-size: 0.825rem; }
|
||||
.submenu-toggle { position: relative; }
|
||||
.submenu-arrow {
|
||||
position: absolute; right: 16px;
|
||||
font-size: 0.7rem; transition: transform 0.2s;
|
||||
}
|
||||
|
||||
/* Toggle switch */
|
||||
.toggle-row {
|
||||
display: flex; align-items: center;
|
||||
justify-content: space-between; cursor: default;
|
||||
}
|
||||
.toggle-switch {
|
||||
position: relative; width: 36px; height: 20px;
|
||||
display: inline-block; flex-shrink: 0;
|
||||
}
|
||||
.toggle-switch input { opacity: 0; width: 0; height: 0; }
|
||||
.toggle-slider {
|
||||
position: absolute; inset: 0; border-radius: 10px;
|
||||
background: rgba(255,255,255,0.15); cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.toggle-slider::before {
|
||||
content: ""; position: absolute;
|
||||
width: 16px; height: 16px; border-radius: 50%;
|
||||
left: 2px; bottom: 2px; background: white;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
.toggle-switch input:checked + .toggle-slider { background: #059669; }
|
||||
.toggle-switch input:checked + .toggle-slider::before { transform: translateX(16px); }
|
||||
.user.light .toggle-slider { background: rgba(0,0,0,0.15); }
|
||||
.user.light .toggle-switch input:checked + .toggle-slider { background: #059669; }
|
||||
`;
|
||||
|
||||
const MODAL_STYLES = `
|
||||
|
|
|
|||
|
|
@ -1140,7 +1140,6 @@
|
|||
<button id="new-feed" title="New Feed from another layer">🔄 Feed</button>
|
||||
<button id="toggle-memory" title="Forgotten rSpaces">💭 Memory</button>
|
||||
<button id="toggle-hide-forgotten" title="Hide forgotten items">👁 Hide Faded</button>
|
||||
<button id="toggle-theme" title="Toggle dark mode">🌙 Dark</button>
|
||||
|
||||
<span class="toolbar-sep"></span>
|
||||
|
||||
|
|
@ -1257,24 +1256,21 @@
|
|||
if (tb) tb.setModules(moduleList);
|
||||
}).catch(() => {});
|
||||
|
||||
// ── Dark mode toggle ──
|
||||
// ── Dark mode (default dark, toggled from My Account dropdown) ──
|
||||
{
|
||||
const savedTheme = localStorage.getItem("canvas-theme") || "light";
|
||||
const themeBtn = document.getElementById("toggle-theme");
|
||||
const savedTheme = localStorage.getItem("canvas-theme") || "dark";
|
||||
|
||||
function applyTheme(theme) {
|
||||
document.body.setAttribute("data-theme", theme);
|
||||
document.querySelector(".rstack-header")?.setAttribute("data-theme", theme);
|
||||
document.querySelector(".rstack-tab-row")?.setAttribute("data-theme", theme);
|
||||
if (themeBtn) themeBtn.textContent = theme === "dark" ? "☀️ Light" : "🌙 Dark";
|
||||
}
|
||||
|
||||
applyTheme(savedTheme);
|
||||
|
||||
themeBtn?.addEventListener("click", () => {
|
||||
const next = document.body.getAttribute("data-theme") === "dark" ? "light" : "dark";
|
||||
applyTheme(next);
|
||||
localStorage.setItem("canvas-theme", next);
|
||||
// Listen for theme changes from rstack-identity component
|
||||
document.addEventListener("theme-change", (e) => {
|
||||
applyTheme(e.detail?.theme || "dark");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue