1 parent
2ac1566eca
commit
1bb398acd1
32 files changed
+907
-136
No files matched your search
@@ -654,6 +654,10 @@ function applyFeatureAvailability() {
|
||||
}
|
||||
$("#diagnostic-sieve-card")?.classList.toggle("hidden", state.authPolicy.sieveEnabled === false);
|
||||
$("#quick-filter-btn")?.classList.toggle("hidden", state.authPolicy.sieveEnabled === false);
|
||||
$("#account-add")?.classList.toggle("hidden", !multiAccountEnabled());
|
||||
renderAccountSwitcher();
|
||||
renderAccountNotificationSettings();
|
||||
renderAccountSettings();
|
||||
}
|
||||
|
||||
async function loadAuthPolicy() {
|
||||
@@ -677,6 +681,103 @@ async function loadAuthPolicy() {
|
||||
});
|
||||
}
|
||||
|
||||
const NOTIFICATION_ACCOUNTS_STORAGE_KEY = "rustbox.notificationAccounts";
|
||||
|
||||
function multiAccountEnabled() {
|
||||
return state.authPolicy.multiAccountEnabled === true;
|
||||
}
|
||||
|
||||
function syncNotificationAccountSelection() {
|
||||
let storedEmails = null;
|
||||
try {
|
||||
const raw = localStorage.getItem(NOTIFICATION_ACCOUNTS_STORAGE_KEY);
|
||||
if (raw !== null) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (Array.isArray(parsed)) storedEmails = new Set(parsed.map((value) => String(value).trim().toLowerCase()).filter(Boolean));
|
||||
}
|
||||
} catch (_) { }
|
||||
const selected = storedEmails === null
|
||||
? state.accounts.map((account) => account.id)
|
||||
: state.accounts
|
||||
.filter((account) => storedEmails.has(String(account.email || "").trim().toLowerCase()))
|
||||
.map((account) => account.id);
|
||||
state.notificationAccountIds = new Set(selected);
|
||||
}
|
||||
|
||||
function saveNotificationAccountSelection() {
|
||||
const selectedEmails = state.accounts
|
||||
.filter((account) => state.notificationAccountIds.has(account.id))
|
||||
.map((account) => String(account.email || "").trim().toLowerCase())
|
||||
.filter(Boolean);
|
||||
localStorage.setItem(NOTIFICATION_ACCOUNTS_STORAGE_KEY, JSON.stringify(selectedEmails));
|
||||
}
|
||||
|
||||
function notificationAccountEnabled(accountId) {
|
||||
if (!accountId) return false;
|
||||
if (!multiAccountEnabled()) return accountId === state.accountId;
|
||||
return state.notificationAccountIds.has(accountId);
|
||||
}
|
||||
|
||||
function renderAccountSwitcher() {
|
||||
const switcher = $("#account-switcher");
|
||||
const menu = $("#account-switcher-menu");
|
||||
if (!switcher || !menu) return;
|
||||
const visible = multiAccountEnabled() && state.accounts.length > 1;
|
||||
if (!visible) {
|
||||
switcher.open = false;
|
||||
switcher.classList.add("hidden");
|
||||
menu.replaceChildren();
|
||||
return;
|
||||
}
|
||||
switcher.classList.remove("hidden");
|
||||
menu.replaceChildren();
|
||||
for (const account of state.accounts) {
|
||||
const button = el("button", account.id === state.accountId ? "active" : "");
|
||||
button.type = "button";
|
||||
button.setAttribute("role", "menuitem");
|
||||
button.append(
|
||||
el("span", "", account.email || "Account"),
|
||||
el("span", "account-switcher-state", account.id === state.accountId ? "Current" : "Switch"),
|
||||
);
|
||||
button.addEventListener("click", async () => {
|
||||
switcher.open = false;
|
||||
if (account.id !== state.accountId) await switchAccount(account.id);
|
||||
});
|
||||
menu.append(button);
|
||||
}
|
||||
}
|
||||
|
||||
function renderAccountNotificationSettings() {
|
||||
const section = $("#multi-account-notifications");
|
||||
const list = $("#account-notification-list");
|
||||
if (!section || !list) return;
|
||||
const visible = multiAccountEnabled() && state.accounts.length > 1;
|
||||
section.classList.toggle("hidden", !visible);
|
||||
list.replaceChildren();
|
||||
if (!visible) return;
|
||||
for (const account of state.accounts) {
|
||||
const row = el("div", "account-settings-row account-notification-row");
|
||||
const details = el("div", "account-session-details");
|
||||
details.append(
|
||||
el("strong", "account-email", account.email || "Account"),
|
||||
el("span", "muted", account.id === state.accountId ? "Current account" : "Signed in account"),
|
||||
);
|
||||
const label = el("label", "checkbox-row");
|
||||
const input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.checked = state.notificationAccountIds.has(account.id);
|
||||
input.addEventListener("change", () => {
|
||||
if (input.checked) state.notificationAccountIds.add(account.id);
|
||||
else state.notificationAccountIds.delete(account.id);
|
||||
saveNotificationAccountSelection();
|
||||
if (input.checked && typeof loadNotificationPreferencesForAccount === "function") void loadNotificationPreferencesForAccount(account.id);
|
||||
});
|
||||
label.append(input, el("span", "", "Notify"));
|
||||
row.append(details, label);
|
||||
list.append(row);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSessionPolicy() {
|
||||
try {
|
||||
const policy = await request("/auth/session-policy", { method: "GET" });
|
||||
@@ -688,6 +789,7 @@ async function loadSessionPolicy() {
|
||||
sieveEnabled: false,
|
||||
diagnosticsEnabled: false,
|
||||
aboutEnabled: false,
|
||||
multiAccountEnabled: false,
|
||||
};
|
||||
}
|
||||
setOpenPgpAvailability(state.authPolicy.pgpEnabled !== false);
|
||||
@@ -702,6 +804,8 @@ async function loadAccounts() {
|
||||
state.accountId = null;
|
||||
localStorage.removeItem("rustbox.activeAccount");
|
||||
state.folders = [];
|
||||
state.notificationAccountIds.clear();
|
||||
state.notificationPrefsByAccount.clear();
|
||||
updateUnreadAppBadge();
|
||||
showLogin();
|
||||
return false;
|
||||
@@ -712,6 +816,9 @@ async function loadAccounts() {
|
||||
}
|
||||
const account = state.accounts.find((item) => item.id === state.accountId) || state.accounts[0];
|
||||
$("#account-email").textContent = account.email;
|
||||
syncNotificationAccountSelection();
|
||||
renderAccountSwitcher();
|
||||
renderAccountNotificationSettings();
|
||||
showApp();
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -725,23 +832,33 @@ function renderAccountSettings() {
|
||||
const list = $("#account-settings-list");
|
||||
if (!list) return;
|
||||
list.replaceChildren();
|
||||
if (!state.accounts.length) {
|
||||
const accounts = multiAccountEnabled()
|
||||
? state.accounts
|
||||
: state.accounts.filter((account) => account.id === state.accountId);
|
||||
if (!accounts.length) {
|
||||
list.append(el("div", "empty-list", "No active accounts."));
|
||||
renderAccountSwitcher();
|
||||
renderAccountNotificationSettings();
|
||||
return;
|
||||
}
|
||||
for (const account of state.accounts) {
|
||||
for (const account of accounts) {
|
||||
const row = el("div", `account-settings-row ${account.id === state.accountId ? "active" : ""}`);
|
||||
const email = el("span", "account-email", account.email || "Account");
|
||||
const use = el("button", "button quiet", account.id === state.accountId ? "Current" : "Switch");
|
||||
use.type = "button";
|
||||
use.disabled = account.id === state.accountId;
|
||||
use.disabled = account.id === state.accountId || !multiAccountEnabled();
|
||||
use.addEventListener("click", () => switchAccount(account.id));
|
||||
const remove = el("button", "button quiet danger", "Remove");
|
||||
remove.type = "button";
|
||||
remove.addEventListener("click", () => removeBrowserAccount(account.id));
|
||||
row.append(email, use, remove);
|
||||
row.append(email, use);
|
||||
if (multiAccountEnabled()) {
|
||||
const remove = el("button", "button quiet danger", "Remove");
|
||||
remove.type = "button";
|
||||
remove.addEventListener("click", () => removeBrowserAccount(account.id));
|
||||
row.append(remove);
|
||||
}
|
||||
list.append(row);
|
||||
}
|
||||
renderAccountSwitcher();
|
||||
renderAccountNotificationSettings();
|
||||
}
|
||||
|
||||
async function loadActiveSessions() {
|
||||
@@ -824,15 +941,19 @@ async function reloadActiveAccount() {
|
||||
}
|
||||
|
||||
async function switchAccount(accountId) {
|
||||
if (!multiAccountEnabled()) return;
|
||||
if (!state.accounts.some((account) => account.id === accountId) || accountId === state.accountId) return;
|
||||
state.accountId = accountId;
|
||||
localStorage.setItem("rustbox.activeAccount", accountId);
|
||||
const account = state.accounts.find((item) => item.id === accountId);
|
||||
if (account) $("#account-email").textContent = account.email;
|
||||
renderAccountSwitcher();
|
||||
renderAccountNotificationSettings();
|
||||
try { await reloadActiveAccount(); } catch (error) { toast(error.message, "error"); }
|
||||
}
|
||||
|
||||
async function removeBrowserAccount(accountId) {
|
||||
if (!multiAccountEnabled()) return;
|
||||
const account = state.accounts.find((item) => item.id === accountId);
|
||||
if (!account || !await appConfirm(`Remove ${account.email} from this browser session?`, { title: "Remove account", confirmLabel: "Remove", danger: true })) return;
|
||||
try {
|
||||
@@ -842,11 +963,14 @@ async function removeBrowserAccount(accountId) {
|
||||
if (!state.accounts.length) return;
|
||||
if (removingCurrent) await reloadActiveAccount();
|
||||
else renderAccountSettings();
|
||||
disconnectRealtime();
|
||||
connectRealtime();
|
||||
} catch (error) { toast(error.message, "error"); }
|
||||
}
|
||||
|
||||
async function addBrowserAccount(event) {
|
||||
event.preventDefault();
|
||||
if (!multiAccountEnabled()) return toast("Multiple accounts are disabled by the RustBox administrator", "warning");
|
||||
const errorBox = $("#add-account-error");
|
||||
const submit = $("#add-account-form button[type=submit]");
|
||||
errorBox.classList.add("hidden");
|
||||
@@ -917,6 +1041,8 @@ async function logout() {
|
||||
try { await request("/auth/logout", { method: "POST", body: "{}" }); } catch (_) { }
|
||||
state.accountId = null;
|
||||
state.accounts = [];
|
||||
state.notificationAccountIds.clear();
|
||||
state.notificationPrefsByAccount.clear();
|
||||
localStorage.removeItem("rustbox.activeAccount");
|
||||
showLogin();
|
||||
}
|
||||
Reference in new issue
Block a user