Files
mail/ui/components/account-mail-preferences.slint
T

268 lines
22 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { VerticalBox, HorizontalBox, ScrollView, TextEdit } from "std-widgets.slint";
import { AccountRow } from "../models.slint";
import { AppTheme } from "../design-system.slint";
import { TypeScale } from "../typography.slint";
import { Button } from "button.slint";
import { LineEdit } from "controls.slint";
import { OnScreenSurface } from "on-screen-surface.slint";
import { SettingsSelectRow, SettingsSwitchRow } from "settings-components.slint";
export struct SignatureChoice { id: string, name: string, text: string }
export struct SenderIdentityChoice { email: string, label: string }
export struct SenderIdentityRow {
email: string,
name: string,
reply-to: string,
primary: bool,
manual: bool,
pending: bool,
wildcard: bool,
// "unknown" | "confirmed" | "rejected"
delivery: string,
error: string,
}
export global AccountMailPreferences {
in-out property <int> account-id: -1;
in-out property <string> account-label;
in-out property <[SignatureChoice]> signatures;
in-out property <[string]> signature-names;
in-out property <[string]> default-names;
in-out property <int> editing-index: -1;
in-out property <string> editing-id;
in-out property <string> signature-name;
in-out property <string> signature-text;
in-out property <int> new-index: 0;
in-out property <int> reply-index: 0;
in-out property <string> fingerprint;
in-out property <bool> sign: false;
in-out property <bool> encrypt: false;
in-out property <string> recipient-keys;
in-out property <string> key-inventory;
in-out property <string> status;
in-out property <bool> busy: false;
in-out property <bool> desktop-supported: true;
in-out property <[SenderIdentityChoice]> sender-identities;
in-out property <[string]> sender-identity-names;
in-out property <int> default-sender-index: 0;
in-out property <string> sender-identity-status;
in-out property <[SenderIdentityRow]> sender-identity-rows;
// "any": the server judges at send time; "wildcard": only addresses inside
// a server *@domain identity; "never": the provider is authoritative.
in-out property <string> sender-identity-policy: "never";
in-out property <string> sender-form-email;
in-out property <string> sender-form-name;
in-out property <string> sender-form-reply-to;
in-out property <bool> sender-form-editing: false;
callback load(int);
callback edit-signature(int);
callback save-signature;
callback delete-signature;
callback save-defaults;
callback refresh-sender-identities;
callback save-default-sender;
callback save-sender-identity;
callback edit-sender-identity(string);
callback remove-sender-identity(string);
callback clear-sender-form;
callback save-security;
callback key-action(string);
callback composer-reset;
callback composer-account-changed(int);
callback composer-signature(int);
in-out property <[string]> composer-signature-names;
in-out property <int> composer-signature-index: 0;
in-out property <string> composer-security;
in-out property <bool> composer-preferences-ready: false;
in-out property <[SenderIdentityChoice]> composer-sender-identities;
in-out property <[string]> composer-sender-names;
in-out property <int> composer-sender-index: 0;
in-out property <string> composer-sender-email;
callback composer-sender(int);
callback read-message;
callback export-message;
callback close-reader;
callback context-changed;
in-out property <bool> reader-open: false;
in-out property <string> reader-status;
in-out property <string> reader-text;
}
export component AccountMailPreferencesPanel inherits OnScreenSurface {
in property <[AccountRow]> accounts;
in property <bool> compact: false;
in-out property <bool> security-tab: false;
background: AppTheme.surface;
border-radius: 10px;
border-width: 1px;
border-color: AppTheme.input-border;
vertical-stretch: 0;
pure function identity-detail(identity: SenderIdentityRow) -> string {
if identity.wildcard {
return @tr("Server allows any address at this domain");
}
if identity.pending {
return @tr("Awaiting verification by the provider; cannot be selected yet");
}
if identity.delivery == "rejected" {
return identity.error != "" ? @tr("The server refused the last message from this address: {}", identity.error) : @tr("The server refused the last message from this address");
}
if identity.primary {
return identity.reply-to != "" ? @tr("Sign-in address · replies go to {}", identity.reply-to) : @tr("Sign-in address");
}
if identity.manual {
if identity.delivery == "confirmed" {
return identity.reply-to != "" ? @tr("Added by you · accepted by the server · replies go to {}", identity.reply-to) : @tr("Added by you · accepted by the server");
}
return identity.reply-to != "" ? @tr("Added by you · not used yet · replies go to {}", identity.reply-to) : @tr("Added by you · not used yet");
}
return identity.reply-to != "" ? @tr("Provided by the server · replies go to {}", identity.reply-to) : @tr("Provided by the server");
}
content := VerticalBox {
padding: 20px; spacing: 12px; alignment: start;
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Sender identities, signatures & OpenPGP"); color: AppTheme.heading; font-size: TypeScale.subtitle; font-weight: 700; wrap: word-wrap; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Choose an account to manage its sender identity, signature, and message protection."); color: AppTheme.secondary; wrap: word-wrap; }
for account in root.accounts : Button {
text: account.email;
primary: AccountMailPreferences.account-id == account.id;
enabled: !AccountMailPreferences.busy;
clicked => { AccountMailPreferences.load(account.id); }
}
if AccountMailPreferences.account-id > 0 : VerticalBox {
spacing: 12px;
Text { min-height: self.preferred-height; vertical-stretch: 0; text: AccountMailPreferences.account-label; color: AppTheme.heading; font-weight: 600; overflow: elide; }
HorizontalBox {
Button { text: @tr("Signatures"); primary: !root.security-tab; clicked => { root.security-tab = false; } }
Button { text: @tr("OpenPGP"); primary: root.security-tab; clicked => { root.security-tab = true; } }
}
if !root.security-tab : VerticalBox {
spacing: 10px;
SettingsSelectRow {
compact: root.compact; title: @tr("Default sender identity"); description: @tr("Preselected for new messages. Your sign-in address is unchanged.");
choices: AccountMailPreferences.sender-identity-names; selected-index <=> AccountMailPreferences.default-sender-index;
title-color: AppTheme.heading; description-color: AppTheme.secondary;
selected(index, value) => { AccountMailPreferences.default-sender-index = index; }
}
HorizontalBox {
Button { text: @tr("Save sender default"); primary: true; enabled: !AccountMailPreferences.busy && AccountMailPreferences.sender-identities.length > 0; clicked => { AccountMailPreferences.save-default-sender(); } }
Button { text: @tr("Refresh sender identities"); enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.refresh-sender-identities(); } }
}
if AccountMailPreferences.sender-identity-status != "" : Text { min-height: self.preferred-height; vertical-stretch: 0; text: AccountMailPreferences.sender-identity-status; color: AppTheme.secondary; wrap: word-wrap; }
for identity in AccountMailPreferences.sender-identity-rows : VerticalBox {
padding: 0px; spacing: 4px;
Text { min-height: self.preferred-height; vertical-stretch: 0; text: identity.name != "" ? identity.name + " <" + identity.email + ">" : identity.email; color: AppTheme.heading; overflow: elide; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: root.identity-detail(identity); color: identity.delivery == "rejected" ? AppTheme.destructive : AppTheme.secondary; wrap: word-wrap; }
if identity.manual : HorizontalBox {
padding: 0px;
alignment: start;
Button { text: @tr("Edit"); enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.edit-sender-identity(identity.email); } }
Button { text: @tr("Remove"); destructive: true; enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.remove-sender-identity(identity.email); } }
}
}
if AccountMailPreferences.sender-identity-policy == "never" : Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Gmail decides which addresses you can send from. Add an address in Gmail settings under Accounts › Send mail as, then refresh sender identities."); color: AppTheme.secondary; wrap: word-wrap; }
if AccountMailPreferences.sender-identity-policy != "never" : VerticalBox {
padding: 0px; spacing: 8px;
Text { min-height: self.preferred-height; vertical-stretch: 0; text: AccountMailPreferences.sender-form-editing ? @tr("Edit sender address") : @tr("Add a sender address"); color: AppTheme.heading; font-weight: 600; }
Text {
min-height: self.preferred-height; vertical-stretch: 0; color: AppTheme.secondary; wrap: word-wrap;
text: AccountMailPreferences.sender-identity-policy == "wildcard"
? @tr("Your server lets you send from any address at the domains shown as wildcards. Add the addresses you want to choose from.")
: @tr("Add an alias or another address your mail server lets you send from. The server decides when you send; Flectar shows whether it accepted the last message.");
}
LineEdit { text <=> AccountMailPreferences.sender-form-email; input-label: @tr("Email address"); placeholder-text: @tr("alias@example.com"); read-only: AccountMailPreferences.sender-form-editing; enabled: !AccountMailPreferences.busy; }
LineEdit { text <=> AccountMailPreferences.sender-form-name; input-label: @tr("Sender name"); placeholder-text: @tr("Name shown to recipients (optional)"); enabled: !AccountMailPreferences.busy; }
LineEdit { text <=> AccountMailPreferences.sender-form-reply-to; input-label: @tr("Reply-to address"); placeholder-text: @tr("Where replies should go (optional)"); enabled: !AccountMailPreferences.busy; }
HorizontalBox {
padding: 0px;
alignment: start;
Button { text: AccountMailPreferences.sender-form-editing ? @tr("Save sender address") : @tr("Add sender address"); primary: true; enabled: !AccountMailPreferences.busy && AccountMailPreferences.sender-form-email != ""; clicked => { AccountMailPreferences.save-sender-identity(); } }
if AccountMailPreferences.sender-form-editing : Button { text: @tr("Cancel"); enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.clear-sender-form(); } }
}
}
Rectangle { height: 1px; background: AppTheme.input-border; }
SettingsSelectRow {
compact: root.compact; title: @tr("Edit signature"); description: @tr("Saved separately for each account.");
choices: AccountMailPreferences.signature-names; selected-index: AccountMailPreferences.editing-index;
title-color: AppTheme.heading; description-color: AppTheme.secondary;
selected(index, value) => { AccountMailPreferences.edit-signature(index); }
}
Button { text: @tr("New signature"); enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.edit-signature(-1); } }
LineEdit { text <=> AccountMailPreferences.signature-name; input-label: @tr("Signature name"); placeholder-text: @tr("Signature name, for example Work"); enabled: !AccountMailPreferences.busy; }
TextEdit { wrap: word-wrap; height: 160px; text <=> AccountMailPreferences.signature-text; placeholder-text: @tr("Your name\nRole · Company\nPhone · Website"); enabled: !AccountMailPreferences.busy; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Plain text works in every email client. You can format the inserted signature in the composer."); color: AppTheme.secondary; wrap: word-wrap; }
HorizontalBox {
Button { text: @tr("Save signature"); primary: true; enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.save-signature(); } }
Button { text: @tr("Delete"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.editing-id != ""; clicked => { AccountMailPreferences.delete-signature(); } }
}
SettingsSelectRow {
compact: root.compact; title: @tr("New messages"); description: @tr("Signature inserted when you start a message.");
choices: AccountMailPreferences.default-names; selected-index <=> AccountMailPreferences.new-index;
title-color: AppTheme.heading; description-color: AppTheme.secondary;
selected(index, value) => { AccountMailPreferences.new-index = index; }
}
SettingsSelectRow {
compact: root.compact; title: @tr("Replies & forwards"); description: @tr("Placed above the quoted message.");
choices: AccountMailPreferences.default-names; selected-index <=> AccountMailPreferences.reply-index;
title-color: AppTheme.heading; description-color: AppTheme.secondary;
selected(index, value) => { AccountMailPreferences.reply-index = index; }
}
Button { text: @tr("Save signature defaults"); enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.save-defaults(); } }
}
if root.security-tab : VerticalBox {
spacing: 10px;
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("OpenPGP protects message bodies and attachments. Digital signatures let recipients check who signed a message."); color: AppTheme.secondary; wrap: word-wrap; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: AccountMailPreferences.desktop-supported ? @tr("Uses installed GnuPG 2.x and its secure key storage. Passphrases are entered in GnuPG's unlock dialog.") : @tr("OpenPGP is unavailable on this mobile build. Protected accounts cannot send without a supported encryption engine."); color: AppTheme.secondary; wrap: word-wrap; }
Button { text: @tr("Create a new key"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("generate"); } }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("New keys expire in two years. GnuPG asks you to protect the private key with a passphrase."); color: AppTheme.secondary; wrap: word-wrap; }
LineEdit { text <=> AccountMailPreferences.fingerprint; input-label: @tr("Your full OpenPGP fingerprint"); placeholder-text: @tr("Your full OpenPGP key fingerprint"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; }
SettingsSwitchRow {
title: @tr("Digitally sign messages"); description: @tr("Proves possession of your private key; does not encrypt the message.");
compact: root.compact; checked: AccountMailPreferences.sign; enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; toggled(value) => { AccountMailPreferences.sign = value; }
title-color: AppTheme.heading; description-color: AppTheme.secondary;
}
SettingsSwitchRow {
title: @tr("Require encryption"); description: @tr("Blocks sending unless every recipient has a verified, usable key.");
compact: root.compact; checked: AccountMailPreferences.encrypt; enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; toggled(value) => { AccountMailPreferences.encrypt = value; }
title-color: AppTheme.heading; description-color: AppTheme.secondary;
}
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Verified recipient keys"); color: AppTheme.heading; font-weight: 600; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Compare fingerprints with recipients through a separate trusted channel, then enter one email=fingerprint per line. Importing a key alone does not verify it."); color: AppTheme.secondary; wrap: word-wrap; }
TextEdit { wrap: word-wrap; height: 130px; text <=> AccountMailPreferences.recipient-keys; placeholder-text: @tr("person@example.org=FULL_FINGERPRINT"); enabled: !AccountMailPreferences.busy; }
Button { text: @tr("Save OpenPGP settings"); primary: true; enabled: !AccountMailPreferences.busy; clicked => { AccountMailPreferences.save-security(); } }
if !root.compact : HorizontalBox {
Button { text: @tr("Import key"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("import"); } }
Button { text: @tr("Export public key"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("export"); } }
Button { text: @tr("Show keys"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("list"); } }
}
if root.compact : VerticalBox {
spacing: 8px;
Button { text: @tr("Import key"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("import"); } }
Button { text: @tr("Export public key"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("export"); } }
Button { text: @tr("Show keys"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("list"); } }
}
Button { text: @tr("Open message file (.eml)"); enabled: !AccountMailPreferences.busy && AccountMailPreferences.desktop-supported; clicked => { AccountMailPreferences.key-action("open"); } }
if AccountMailPreferences.key-inventory != "" : TextEdit { wrap: word-wrap; height: 150px; read-only: true; text: AccountMailPreferences.key-inventory; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Manage expiry, revocation and private-key backups in GnuPG. Only public keys are exported here."); color: AppTheme.secondary; wrap: word-wrap; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Subjects and addresses remain visible. Protected drafts stay on this device. Existing server drafts are not retroactively protected. S/MIME certificates are not supported."); color: AppTheme.secondary; wrap: word-wrap; }
}
if AccountMailPreferences.busy || AccountMailPreferences.status != "" : Text {
text: AccountMailPreferences.busy ? @tr("Working…") : AccountMailPreferences.status;
color: AppTheme.secondary; wrap: word-wrap;
}
}
}
}
export component OpenPgpReader inherits Rectangle {
visible: AccountMailPreferences.reader-open;
background: AppTheme.surface;
VerticalBox {
padding: 24px; spacing: 12px;
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("OpenPGP message"); color: AppTheme.heading; font-size: TypeScale.subtitle; font-weight: 700; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: AccountMailPreferences.reader-status; color: AppTheme.secondary; wrap: word-wrap; }
TextEdit { wrap: word-wrap; vertical-stretch: 1; read-only: true; text: AccountMailPreferences.reader-text; }
Text { min-height: self.preferred-height; vertical-stretch: 0; text: @tr("Decrypted content is shown as text, without remote content. It is not added to search or saved to the mail database."); color: AppTheme.secondary; wrap: word-wrap; }
Button { text: @tr("Export decrypted message"); enabled: AccountMailPreferences.reader-text != ""; clicked => { AccountMailPreferences.export-message(); } }
Button { text: @tr("Close"); clicked => { AccountMailPreferences.close-reader(); AccountMailPreferences.reader-open = false; AccountMailPreferences.reader-text = ""; AccountMailPreferences.reader-status = ""; } }
}
}