From 693dc2af6eb797bd22129bdc796f7aa04d945d84 Mon Sep 17 00:00:00 2001 From: Nico de Haen Date: Fri, 15 May 2026 22:27:14 +0200 Subject: [PATCH 1/4] feat: add option to force encryption --- _locales/_untranslated_en.json | 5 ++++- packages/frontend/scss/login/_login.scss | 16 ++++++++++++++ .../frontend/src/components/LoginForm.tsx | 21 ++++++++++++++++++- packages/frontend/src/stores/settings.ts | 6 ++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/_locales/_untranslated_en.json b/_locales/_untranslated_en.json index 7021035cec..1e70e70c54 100644 --- a/_locales/_untranslated_en.json +++ b/_locales/_untranslated_en.json @@ -53,7 +53,7 @@ "prevent_dialog_spam_checkbox": { "message": "Don't allow the app to open this dialog again" }, - "react_more_emojis" : { + "react_more_emojis": { "message": "More emojis..." }, "edit_channel": { @@ -64,5 +64,8 @@ }, "paste_in_qr_scanner": { "message": "Click to paste it in QR scanner" + }, + "enforce_e2ee": { + "message": "Enforce Encryption" } } diff --git a/packages/frontend/scss/login/_login.scss b/packages/frontend/scss/login/_login.scss index 0ea23e28e6..d76f30a2ac 100644 --- a/packages/frontend/scss/login/_login.scss +++ b/packages/frontend/scss/login/_login.scss @@ -152,6 +152,22 @@ div.delta-form-group { } } + &.delta-switch { + > label { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + width: 100%; + height: auto; + font-size: 15px; + line-height: normal; + cursor: pointer; + color: var(--deltaChatPrimaryFgLight); + padding-inline-end: 5px; + } + } + &.delta-input { input { appearance: none; diff --git a/packages/frontend/src/components/LoginForm.tsx b/packages/frontend/src/components/LoginForm.tsx index 4419bd5717..9721a23dbd 100644 --- a/packages/frontend/src/components/LoginForm.tsx +++ b/packages/frontend/src/components/LoginForm.tsx @@ -12,6 +12,8 @@ import { I18nContext } from '../contexts/I18nContext' const log = getLogger('renderer/loginForm') import type { Credentials } from './Settings/DefaultCredentials' +import SettingsStoreInstance, { useSettingsStore } from '../stores/settings' +import Switch from './Switch' const Socket = { automatic: 'automatic', @@ -42,6 +44,8 @@ export default function LoginForm({ const [providerInfo, setProviderInfo] = useState< Type.ProviderInfo | undefined >() + const settingsStore = useSettingsStore()[0] + const forceEncryption = settingsStore?.settings['force_encryption'] !== '0' const handleCredentialsChange = ( event: React.ChangeEvent @@ -260,7 +264,22 @@ export default function LoginForm({ - +
+ +
{ settings['ui.mentions_enabled'] = mentionsEnabledDefaultVal } + if (settings['force_encryption'] == null) { + settings['force_encryption'] = '1' + } + const rc = runtime.getRC_Config() this.reducer.setState({ settings, From a54d93d4f3c25f8d1baaef315287465d1d6f294b Mon Sep 17 00:00:00 2001 From: Nico de Haen Date: Wed, 27 May 2026 08:10:46 +0200 Subject: [PATCH 2/4] fix: only save e2ee setting after successful login --- packages/frontend/scss/login/_login.scss | 3 + .../frontend/src/components/LoginForm.tsx | 43 +++++++++------ .../dialogs/EditAccountAndPasswordDialog.tsx | 31 ++++++++++- .../components/screens/AccountSetupScreen.tsx | 55 +++++++++++++------ packages/frontend/src/stores/settings.ts | 4 -- 5 files changed, 95 insertions(+), 41 deletions(-) diff --git a/packages/frontend/scss/login/_login.scss b/packages/frontend/scss/login/_login.scss index d76f30a2ac..1d659392ba 100644 --- a/packages/frontend/scss/login/_login.scss +++ b/packages/frontend/scss/login/_login.scss @@ -153,6 +153,8 @@ div.delta-form-group { } &.delta-switch { + overflow: visible; + > label { display: flex; flex-direction: row; @@ -165,6 +167,7 @@ div.delta-form-group { cursor: pointer; color: var(--deltaChatPrimaryFgLight); padding-inline-end: 5px; + padding-block-end: 4px; } } diff --git a/packages/frontend/src/components/LoginForm.tsx b/packages/frontend/src/components/LoginForm.tsx index 9721a23dbd..52b9b3ac76 100644 --- a/packages/frontend/src/components/LoginForm.tsx +++ b/packages/frontend/src/components/LoginForm.tsx @@ -31,6 +31,8 @@ const CertificateChecks = { type LoginProps = React.PropsWithChildren<{ credentials: Credentials setCredentials: (credentials: Credentials) => void + forceEncryption?: boolean + setForceEncryption?: (forceEncryption: boolean) => void /** whether editing existing account */ isEdit?: true }> @@ -38,6 +40,8 @@ type LoginProps = React.PropsWithChildren<{ export default function LoginForm({ credentials, setCredentials, + forceEncryption: forceEncryptionProp, + setForceEncryption, isEdit, }: LoginProps) { const [uiShowAdvanced, setUiShowAdvanced] = useState(false) @@ -45,7 +49,8 @@ export default function LoginForm({ Type.ProviderInfo | undefined >() const settingsStore = useSettingsStore()[0] - const forceEncryption = settingsStore?.settings['force_encryption'] !== '0' + const forceEncryption = + forceEncryptionProp ?? settingsStore?.settings['force_encryption'] === '1' const handleCredentialsChange = ( event: React.ChangeEvent @@ -264,22 +269,6 @@ export default function LoginForm({ -
- -
+
+ +

diff --git a/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx b/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx index 1891e18c05..16b94720be 100644 --- a/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx +++ b/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx @@ -16,7 +16,7 @@ import useDialog from '../../hooks/dialog/useDialog' import type { DialogProps } from '../../contexts/DialogContext' import AlertDialog from './AlertDialog' import { T } from '@deltachat/jsonrpc-client' -import { useSettingsStore } from '../../stores/settings' +import SettingsStoreInstance, { useSettingsStore } from '../../stores/settings' import { getLogger } from '@deltachat-desktop/shared/logger' type AccountAndPasswordDialogProps = DialogProps & { @@ -57,11 +57,15 @@ function EditAccountInner({ onClose: DialogProps['onClose'] addr?: string }) { + const settingsStore = useSettingsStore()[0] const [initialSettings, setInitialAccountSettings] = useState(defaultCredentials()) const [accountSettings, setAccountSettings] = useState(defaultCredentials()) + const [forceEncryption, setForceEncryption] = useState( + settingsStore?.settings['force_encryption'] !== '0' + ) const { openDialog } = useDialog() const tx = useTranslationFunction() @@ -105,7 +109,18 @@ function EditAccountInner({ }, [addr]) const onUpdate = useCallback(async () => { - const onSuccess = () => onClose() + const onSuccess = async () => { + const forceEncryptionValue = forceEncryption ? '1' : '0' + if ( + settingsStore?.settings['force_encryption'] !== forceEncryptionValue + ) { + await SettingsStoreInstance.effect.setCoreSetting( + 'force_encryption', + forceEncryptionValue + ) + } + onClose() + } const update = () => { openDialog(ConfigureProgressDialog, { @@ -121,8 +136,16 @@ function EditAccountInner({ log.error('changing email addres of transport is not allowed') return } + update() - }, [accountSettings, initialSettings, onClose, openDialog]) + }, [ + accountSettings, + forceEncryption, + initialSettings, + onClose, + openDialog, + settingsStore, + ]) const onOk = useCallback(async () => { await onUpdate() @@ -137,6 +160,8 @@ function EditAccountInner({ )} diff --git a/packages/frontend/src/components/screens/AccountSetupScreen.tsx b/packages/frontend/src/components/screens/AccountSetupScreen.tsx index 00344605c5..e0043fc559 100644 --- a/packages/frontend/src/components/screens/AccountSetupScreen.tsx +++ b/packages/frontend/src/components/screens/AccountSetupScreen.tsx @@ -16,6 +16,7 @@ import useTranslationFunction from '../../hooks/useTranslationFunction' import useDialog from '../../hooks/dialog/useDialog' import { DialogId } from '../../contexts/DialogContext' import AlertDialog from '../dialogs/AlertDialog' +import SettingsStoreInstance, { useSettingsStore } from '../../stores/settings' import type ScreenController from '../../ScreenController' @@ -32,25 +33,43 @@ export default function AccountSetupScreen({ const [credentials, setCredentials] = useState(defaultCredentials()) - - const onClickLogin = useCallback( - () => - openDialog(ConfigureProgressDialog, { - credentials, - onSuccess: () => { - selectAccount(accountId) - }, - onFail: (error: string) => - setPromptDialogId( - openDialog(AlertDialog, { - message: error, - cb: () => setPromptDialogId(null), - }) - ), - }), - [accountId, openDialog, selectAccount, credentials] + const settingsStore = useSettingsStore()[0] + const [forceEncryption, setForceEncryption] = useState( + settingsStore?.settings['force_encryption'] !== '0' ) + const onClickLogin = useCallback(() => { + openDialog(ConfigureProgressDialog, { + credentials, + onSuccess: async () => { + const forceEncryptionValue = forceEncryption ? '1' : '0' + if ( + settingsStore?.settings['force_encryption'] !== forceEncryptionValue + ) { + await SettingsStoreInstance.effect.setCoreSetting( + 'force_encryption', + forceEncryptionValue + ) + } + selectAccount(accountId) + }, + onFail: (error: string) => + setPromptDialogId( + openDialog(AlertDialog, { + message: error, + cb: () => setPromptDialogId(null), + }) + ), + }) + }, [ + accountId, + credentials, + forceEncryption, + openDialog, + selectAccount, + settingsStore, + ]) + // TODO(maxph): we're now using and can submit result via input // and not an explicit keyboard handling const onKeyDown = useCallback( @@ -88,6 +107,8 @@ export default function AccountSetupScreen({ diff --git a/packages/frontend/src/stores/settings.ts b/packages/frontend/src/stores/settings.ts index f572fef392..69a7239cba 100644 --- a/packages/frontend/src/stores/settings.ts +++ b/packages/frontend/src/stores/settings.ts @@ -134,10 +134,6 @@ class SettingsStore extends Store { settings['ui.mentions_enabled'] = mentionsEnabledDefaultVal } - if (settings['force_encryption'] == null) { - settings['force_encryption'] = '1' - } - const rc = runtime.getRC_Config() this.reducer.setState({ settings, From 36ff1d5b3be404ba84059100993b8f50d783e936 Mon Sep 17 00:00:00 2001 From: Nico de Haen Date: Wed, 27 May 2026 08:23:23 +0200 Subject: [PATCH 3/4] chore: remove e2ee key from untranslated --- _locales/_untranslated_en.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/_locales/_untranslated_en.json b/_locales/_untranslated_en.json index 1e70e70c54..a2ad9e0cd4 100644 --- a/_locales/_untranslated_en.json +++ b/_locales/_untranslated_en.json @@ -64,8 +64,5 @@ }, "paste_in_qr_scanner": { "message": "Click to paste it in QR scanner" - }, - "enforce_e2ee": { - "message": "Enforce Encryption" } } From 26ea29447203043efa7ceb45803a9c4fabb4b467 Mon Sep 17 00:00:00 2001 From: Nico de Haen Date: Wed, 27 May 2026 17:36:33 +0200 Subject: [PATCH 4/4] Some small cleanups --- packages/frontend/scss/login/_login.scss | 2 -- packages/frontend/src/components/LoginForm.tsx | 6 +----- .../src/components/dialogs/EditAccountAndPasswordDialog.tsx | 2 +- .../frontend/src/components/screens/AccountSetupScreen.tsx | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/frontend/scss/login/_login.scss b/packages/frontend/scss/login/_login.scss index 1d659392ba..068dd965dd 100644 --- a/packages/frontend/scss/login/_login.scss +++ b/packages/frontend/scss/login/_login.scss @@ -153,8 +153,6 @@ div.delta-form-group { } &.delta-switch { - overflow: visible; - > label { display: flex; flex-direction: row; diff --git a/packages/frontend/src/components/LoginForm.tsx b/packages/frontend/src/components/LoginForm.tsx index 52b9b3ac76..d2eefde7b3 100644 --- a/packages/frontend/src/components/LoginForm.tsx +++ b/packages/frontend/src/components/LoginForm.tsx @@ -12,7 +12,7 @@ import { I18nContext } from '../contexts/I18nContext' const log = getLogger('renderer/loginForm') import type { Credentials } from './Settings/DefaultCredentials' -import SettingsStoreInstance, { useSettingsStore } from '../stores/settings' +import { useSettingsStore } from '../stores/settings' import Switch from './Switch' const Socket = { @@ -295,10 +295,6 @@ export default function LoginForm({ setForceEncryption(!forceEncryption) return } - SettingsStoreInstance.effect.setCoreSetting( - 'force_encryption', - forceEncryption ? '0' : '1' - ) }} /> diff --git a/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx b/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx index 16b94720be..eff0618965 100644 --- a/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx +++ b/packages/frontend/src/components/dialogs/EditAccountAndPasswordDialog.tsx @@ -64,7 +64,7 @@ function EditAccountInner({ const [accountSettings, setAccountSettings] = useState(defaultCredentials()) const [forceEncryption, setForceEncryption] = useState( - settingsStore?.settings['force_encryption'] !== '0' + settingsStore?.settings['force_encryption'] === '1' ) const { openDialog } = useDialog() diff --git a/packages/frontend/src/components/screens/AccountSetupScreen.tsx b/packages/frontend/src/components/screens/AccountSetupScreen.tsx index e0043fc559..5ce5fafa3f 100644 --- a/packages/frontend/src/components/screens/AccountSetupScreen.tsx +++ b/packages/frontend/src/components/screens/AccountSetupScreen.tsx @@ -35,7 +35,7 @@ export default function AccountSetupScreen({ useState(defaultCredentials()) const settingsStore = useSettingsStore()[0] const [forceEncryption, setForceEncryption] = useState( - settingsStore?.settings['force_encryption'] !== '0' + settingsStore?.settings['force_encryption'] === '1' ) const onClickLogin = useCallback(() => {