v0.2.17
RustBox CI / rustbox (push) Canceled after 0s

This commit is contained in:
Mateusz Gruszczyński committed 2026-11-03 10:35:05 +01:00
1 parent 2ac1566eca
commit 1bb398acd1
32 files changed
+907 -136

No files matched your search

+133 -7
View File
@@ -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();
}