1 parent
5b22add1f3
commit
2c98b8f925
38 files changed
+915
-87
No files matched your search
@@ -25,6 +25,15 @@
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
|
||||
.settings-shortcuts {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.settings-panel[data-feature-disabled] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.settings-tab {
|
||||
min-height: 30px;
|
||||
border: 1px solid transparent;
|
||||
|
||||
+8
-4
@@ -567,7 +567,7 @@
|
||||
data-i18n="filters">Filters</button><button class="settings-tab" data-tab="diagnostics"
|
||||
type="button">Diagnostics</button><button class="settings-tab" data-tab="account"
|
||||
type="button">Account</button><button class="settings-tab" data-tab="about" type="button"
|
||||
data-i18n="about">About</button></nav>
|
||||
data-i18n="about">About</button><button id="shortcuts-open" class="button quiet settings-shortcuts" type="button">Keyboard shortcuts</button></nav>
|
||||
<div class="settings-content">
|
||||
<section class="settings-panel" data-panel="appearance">
|
||||
<div class="settings-panel-heading">
|
||||
@@ -1120,7 +1120,7 @@
|
||||
</div>
|
||||
<div id="diagnostic-smtp-summary" class="capability-list"></div>
|
||||
</section>
|
||||
<section class="diagnostic-card">
|
||||
<section id="diagnostic-sieve-card" class="diagnostic-card">
|
||||
<div class="section-heading"><span>ManageSieve</span><strong id="diagnostic-sieve-status">Not
|
||||
checked</strong></div>
|
||||
<div id="diagnostic-sieve-summary" class="capability-list"></div>
|
||||
@@ -1162,6 +1162,12 @@
|
||||
sessions</button>
|
||||
</div>
|
||||
<div id="account-session-list" class="account-settings-list"></div>
|
||||
<div class="settings-panel-heading account-session-heading">
|
||||
<div>
|
||||
<h3>Reset settings</h3>
|
||||
<p class="muted">Restore appearance, mailbox, notifications, folder, calendar and OpenPGP preferences. Messages, identities, keys and templates are kept.</p>
|
||||
</div><button id="reset-all-settings" class="button quiet danger" type="button">Reset all settings</button>
|
||||
</div>
|
||||
</section>
|
||||
<section class="settings-panel hidden" data-panel="about">
|
||||
<h2>RustBox __RUSTBOX_VERSION__</h2>
|
||||
@@ -1179,8 +1185,6 @@
|
||||
<dd><a href="https://git.linuxiarz.pl/gru/RustBox" target="_blank"
|
||||
rel="noopener noreferrer">git.linuxiarz.pl/gru/RustBox</a></dd>
|
||||
</dl>
|
||||
<div class="inline-actions"><button id="shortcuts-open" class="button quiet" type="button">Keyboard
|
||||
shortcuts</button></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
pgpKeys: [],
|
||||
pgpPrefs: { wkdEnabled: true, hkpEnabled: false, requireTrustedEncryption: false },
|
||||
pgpRecipientStatus: [],
|
||||
authPolicy: { rememberMeEnabled: true, rememberMeDays: 30, sessionTimeoutHours: 24, pgpEnabled: true },
|
||||
authPolicy: { rememberMeEnabled: true, rememberMeDays: 30, sessionTimeoutHours: 24, pgpEnabled: true, sieveEnabled: true, diagnosticsEnabled: true, aboutEnabled: true },
|
||||
contacts: [],
|
||||
trustedSenders: new Set(),
|
||||
contactGroups: [],
|
||||
|
||||
@@ -458,13 +458,35 @@ function showApp() {
|
||||
$("#app-view").classList.remove("hidden");
|
||||
}
|
||||
|
||||
function settingsTabAvailable(name) {
|
||||
if (name === "openpgp") return state.authPolicy.pgpEnabled !== false;
|
||||
if (name === "sieve") return state.authPolicy.sieveEnabled !== false;
|
||||
if (name === "diagnostics") return state.authPolicy.diagnosticsEnabled !== false;
|
||||
if (name === "about") return state.authPolicy.aboutEnabled !== false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function applyFeatureAvailability() {
|
||||
for (const [name, enabled] of [
|
||||
["sieve", state.authPolicy.sieveEnabled !== false],
|
||||
["diagnostics", state.authPolicy.diagnosticsEnabled !== false],
|
||||
["about", state.authPolicy.aboutEnabled !== false],
|
||||
]) {
|
||||
$(`.settings-tab[data-tab="${name}"]`)?.classList.toggle("hidden", !enabled);
|
||||
$(`.settings-panel[data-panel="${name}"]`)?.toggleAttribute("data-feature-disabled", !enabled);
|
||||
}
|
||||
$("#diagnostic-sieve-card")?.classList.toggle("hidden", state.authPolicy.sieveEnabled === false);
|
||||
$("#quick-filter-btn")?.classList.toggle("hidden", state.authPolicy.sieveEnabled === false);
|
||||
}
|
||||
|
||||
async function loadAuthPolicy() {
|
||||
try {
|
||||
state.authPolicy = await request("/auth/policy", { method: "GET" });
|
||||
} catch (_) {
|
||||
state.authPolicy = { rememberMeEnabled: true, rememberMeDays: 30, sessionTimeoutHours: 24, pgpEnabled: true };
|
||||
state.authPolicy = { rememberMeEnabled: true, rememberMeDays: 30, sessionTimeoutHours: 24, pgpEnabled: true, sieveEnabled: true, diagnosticsEnabled: true, aboutEnabled: true };
|
||||
}
|
||||
setOpenPgpAvailability(state.authPolicy.pgpEnabled !== false);
|
||||
applyFeatureAvailability();
|
||||
const enabled = state.authPolicy.rememberMeEnabled !== false;
|
||||
["#login-remember-row", "#add-account-remember-row"].forEach((selector) => $(selector)?.classList.toggle("hidden", !enabled));
|
||||
["#login-remember", "#add-account-remember"].forEach((selector) => { const input = $(selector); if (input && !enabled) { input.checked = false; input.disabled = true; } });
|
||||
|
||||
@@ -500,9 +500,11 @@ function openMessageContextMenu(x, y, message) {
|
||||
{ label: "Add label…", action: async () => { const tag = await promptTag("Add label"); if (tag) await addTagToMessage(message, tag); } },
|
||||
{ label: "Remove label…", action: async () => { const attached = state.tags.filter((tag) => (message.tags || []).some((item) => item.id === tag.id)); const tag = await promptTag("Remove label", attached); if (tag) await removeTagFromMessage(message, tag); } },
|
||||
{ label: isJunkFolder(message.folder || state.folder) ? tr("mark_not_spam", "Mark as not spam") : tr("mark_spam", "Mark as spam"), action: async () => { await ensureMessageOpen(message); await junkCurrent(); } },
|
||||
{ label: "Create filter from sender", action: async () => { await ensureMessageOpen(message); await createFilterFromCurrentMessage("sender"); } },
|
||||
{ label: "Create filter from domain", action: async () => { await ensureMessageOpen(message); await createFilterFromCurrentMessage("domain"); } },
|
||||
{ label: "Create filter from subject", action: async () => { await ensureMessageOpen(message); await createFilterFromCurrentMessage("subject"); } },
|
||||
...(state.authPolicy.sieveEnabled === false ? [] : [
|
||||
{ label: "Create filter from sender", action: async () => { await ensureMessageOpen(message); await createFilterFromCurrentMessage("sender"); } },
|
||||
{ label: "Create filter from domain", action: async () => { await ensureMessageOpen(message); await createFilterFromCurrentMessage("domain"); } },
|
||||
{ label: "Create filter from subject", action: async () => { await ensureMessageOpen(message); await createFilterFromCurrentMessage("subject"); } },
|
||||
]),
|
||||
{ label: "Add sender to contacts", action: async () => { await ensureMessageOpen(message); await addSenderToContacts(); } },
|
||||
{ label: "Search messages from sender", action: async () => { await ensureMessageOpen(message); await searchFromCurrentSender(); } },
|
||||
{ label: "Unsubscribe from mailing list", action: async () => { await ensureMessageOpen(message); await openListUnsubscribe(); } },
|
||||
@@ -835,7 +837,9 @@ function openCurrentSenderMenu(x, y) {
|
||||
{ label: "Copy address", action: copySenderAddress },
|
||||
{ label: "Search messages", action: searchFromCurrentSender },
|
||||
"separator",
|
||||
{ label: "Create filter", action: async () => createFilterFromCurrentMessage("sender") },
|
||||
...(state.authPolicy.sieveEnabled === false ? [] : [
|
||||
{ label: "Create filter", action: async () => createFilterFromCurrentMessage("sender") },
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -985,6 +989,7 @@ async function prepareQuickFilterAdvanced(config) {
|
||||
}
|
||||
|
||||
async function createFilterFromCurrentMessage(kind = "sender") {
|
||||
if (state.authPolicy.sieveEnabled === false) return;
|
||||
const detail = state.currentDetail;
|
||||
if (!detail) return;
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ async function loadQuota() {
|
||||
}
|
||||
|
||||
function openSettings(tab = "appearance") {
|
||||
if (tab === "openpgp" && state.authPolicy.pgpEnabled === false) tab = "appearance";
|
||||
if (!settingsTabAvailable(tab)) tab = "appearance";
|
||||
selectSettingsTab(tab);
|
||||
$("#settings-dialog").showModal();
|
||||
if (tab === "sieve") loadSieve();
|
||||
@@ -59,7 +59,7 @@ function openSettings(tab = "appearance") {
|
||||
}
|
||||
|
||||
function selectSettingsTab(name) {
|
||||
if (name === "openpgp" && state.authPolicy.pgpEnabled === false) name = "appearance";
|
||||
if (!settingsTabAvailable(name)) name = "appearance";
|
||||
$$(".settings-tab").forEach((button) => button.classList.toggle("active", button.dataset.tab === name));
|
||||
$$(".settings-panel").forEach((panel) => panel.classList.toggle("hidden", panel.dataset.panel !== name));
|
||||
}
|
||||
@@ -371,6 +371,20 @@ function disconnectRealtime() {
|
||||
if (socket && socket.readyState < WebSocket.CLOSING) socket.close(1000, "Client disconnect");
|
||||
}
|
||||
|
||||
async function resetAllSettings() {
|
||||
if (!window.confirm("Reset all RustBox settings to defaults? Messages, identities, keys and templates will be kept.")) return;
|
||||
const button = $("#reset-all-settings");
|
||||
if (button) button.disabled = true;
|
||||
try {
|
||||
await request("/settings/reset", { method: "POST", body: "{}" });
|
||||
["rustbox.calendarView", "rustbox.threadExpanded", "rustbox.composeFullscreen"].forEach((key) => localStorage.removeItem(key));
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
toast(error.message, "error");
|
||||
if (button) button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function capabilityBadge(label, supported) {
|
||||
return `<span class="capability-badge ${supported ? "supported" : "unsupported"}">${escapeHtml(label)}<strong>${supported ? "yes" : "no"}</strong></span>`;
|
||||
}
|
||||
@@ -410,13 +424,15 @@ async function loadDiagnostics() {
|
||||
smtpStatus.textContent = "Unavailable";
|
||||
$("#diagnostic-smtp-summary").textContent = error.message;
|
||||
}
|
||||
try {
|
||||
const info = await request("/sieve/status", { method: "GET" });
|
||||
sieveStatus.textContent = info.connected ? "Connected" : "Unavailable";
|
||||
$("#diagnostic-sieve-summary").textContent = `${info.tlsMode || ""}${info.implementation ? ` · ${info.implementation}` : ""}${info.extensions?.length ? ` · ${info.extensions.join(", ")}` : ""}`;
|
||||
} catch (error) {
|
||||
sieveStatus.textContent = "Unavailable";
|
||||
$("#diagnostic-sieve-summary").textContent = error.message;
|
||||
if (state.authPolicy.sieveEnabled !== false) {
|
||||
try {
|
||||
const info = await request("/sieve/status", { method: "GET" });
|
||||
sieveStatus.textContent = info.connected ? "Connected" : "Unavailable";
|
||||
$("#diagnostic-sieve-summary").textContent = `${info.tlsMode || ""}${info.implementation ? ` · ${info.implementation}` : ""}${info.extensions?.length ? ` · ${info.extensions.join(", ")}` : ""}`;
|
||||
} catch (error) {
|
||||
sieveStatus.textContent = "Unavailable";
|
||||
$("#diagnostic-sieve-summary").textContent = error.message;
|
||||
}
|
||||
}
|
||||
try {
|
||||
const info = await request("/diagnostics/sync", { method: "GET" });
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
async function loadSieve() {
|
||||
if (state.authPolicy.sieveEnabled === false) return;
|
||||
const status = $("#sieve-status");
|
||||
status.textContent = "Connecting…";
|
||||
try {
|
||||
|
||||
@@ -246,6 +246,7 @@ function bindEvents() {
|
||||
$("#shortcuts-open").addEventListener("click", () => $("#shortcuts-dialog").showModal());
|
||||
$("#account-add").addEventListener("click", () => { $("#add-account-error").classList.add("hidden"); $("#add-account-dialog").showModal(); setTimeout(() => $("#add-account-email").focus(), 20); });
|
||||
$("#account-revoke-others").addEventListener("click", revokeOtherSessions);
|
||||
$("#reset-all-settings").addEventListener("click", resetAllSettings);
|
||||
$("#add-account-form").addEventListener("submit", addBrowserAccount);
|
||||
$("#mobile-folders").addEventListener("click", toggleFolderDrawer);
|
||||
$("#folder-drawer-close").addEventListener("click", () => setFolderDrawerOpen(false));
|
||||
|
||||
Reference in new issue
Block a user