From 6467943898fc22614c4b99aa0e0599b81459ba26 Mon Sep 17 00:00:00 2001
From: Florian Duros <florian.duros@ormaz.fr>
Date: Mon, 23 Dec 2024 15:52:22 +0100
Subject: [PATCH] Add the `Never send encrypted messages to unverified devices`
 settings

---
 .../settings/encryption/_AdvancedPanel.pcss   | 19 +++++----
 .../settings/encryption/AdvancedPanel.tsx     | 41 ++++++++++++++++++-
 src/i18n/strings/en_EN.json                   |  3 ++
 3 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/res/css/views/settings/encryption/_AdvancedPanel.pcss b/res/css/views/settings/encryption/_AdvancedPanel.pcss
index 46cccac5d44..d99afe016a1 100644
--- a/res/css/views/settings/encryption/_AdvancedPanel.pcss
+++ b/res/css/views/settings/encryption/_AdvancedPanel.pcss
@@ -5,25 +5,30 @@
  * Please see LICENSE files in the repository root for full details.
  */
 
-.mx_AdvancedPanel_Details {
+.mx_AdvancedPanel_Details,
+.mx_OtherSettings {
     display: flex;
     flex-direction: column;
     gap: var(--cpd-space-6x);
     width: 100%;
     align-items: start;
 
+    .mx_AdvancedPanel_Details_title,
+    .mx_OtherSettings_title {
+        font: var(--cpd-font-body-lg-semibold);
+        padding-bottom: var(--cpd-space-2x);
+        border-bottom: 1px solid var(--cpd-color-gray-400);
+        width: 100%;
+    }
+}
+
+.mx_AdvancedPanel_Details {
     .mx_AdvancedPanel_Details_content {
         display: flex;
         flex-direction: column;
         gap: var(--cpd-space-4x);
         width: 100%;
 
-        > span {
-            font: var(--cpd-font-body-lg-semibold);
-            padding-bottom: var(--cpd-space-2x);
-            border-bottom: 1px solid var(--cpd-color-gray-400);
-        }
-
         > div {
             display: flex;
 
diff --git a/src/components/views/settings/encryption/AdvancedPanel.tsx b/src/components/views/settings/encryption/AdvancedPanel.tsx
index 96b3031b3b5..8480db4d66b 100644
--- a/src/components/views/settings/encryption/AdvancedPanel.tsx
+++ b/src/components/views/settings/encryption/AdvancedPanel.tsx
@@ -6,7 +6,7 @@
  */
 
 import React, { JSX, lazy, MouseEventHandler } from "react";
-import { Button, InlineSpinner } from "@vector-im/compound-web";
+import { Button, HelpMessage, InlineField, InlineSpinner, Label, Root, ToggleControl } from "@vector-im/compound-web";
 import DownloadIcon from "@vector-im/compound-design-tokens/assets/web/icons/download";
 import ShareIcon from "@vector-im/compound-design-tokens/assets/web/icons/share";
 
@@ -15,6 +15,9 @@ import { SettingsSection } from "../shared/SettingsSection";
 import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext";
 import { useAsyncMemo } from "../../../../hooks/useAsyncMemo";
 import Modal from "../../../../Modal";
+import { SettingLevel } from "../../../../settings/SettingLevel";
+import { useSettingValueAt } from "../../../../hooks/useSettings";
+import SettingsStore from "../../../../settings/SettingsStore";
 
 interface AdvancedPanelProps {
     /**
@@ -30,6 +33,7 @@ export function AdvancedPanel({ onResetIdentityClick }: AdvancedPanelProps): JSX
     return (
         <SettingsSection heading={_t("settings|encryption|advanced|title")} legacy={false}>
             <EncryptionDetails onResetIdentityClick={onResetIdentityClick} />
+            <OtherSettings />
         </SettingsSection>
     );
 }
@@ -59,7 +63,9 @@ function EncryptionDetails({ onResetIdentityClick }: EncryptionDetails): JSX.Ele
     return (
         <div className="mx_AdvancedPanel_Details">
             <div className="mx_AdvancedPanel_Details_content">
-                <span>{_t("settings|encryption|advanced|details_title")}</span>
+                <span className="mx_AdvancedPanel_Details_title">
+                    {_t("settings|encryption|advanced|details_title")}
+                </span>
                 <div>
                     <span>{_t("settings|encryption|advanced|session_id")}</span>
                     <span>{matrixClient.deviceId}</span>
@@ -107,3 +113,34 @@ function EncryptionDetails({ onResetIdentityClick }: EncryptionDetails): JSX.Ele
         </div>
     );
 }
+
+/**
+ * Display the never send encrypted message to unverified devices setting.
+ */
+function OtherSettings(): JSX.Element | null {
+    const blacklistUnverifiedDevices = useSettingValueAt<boolean>(SettingLevel.DEVICE, "blacklistUnverifiedDevices");
+    const canSetValue = SettingsStore.canSetValue("blacklistUnverifiedDevices", null, SettingLevel.DEVICE);
+
+    if (!canSetValue) return null;
+
+    return (
+        <Root
+            className="mx_OtherSettings"
+            onChange={async (evt) => {
+                const checked = new FormData(evt.currentTarget).get("neverSendEncrypted") === "on";
+                await SettingsStore.setValue("blacklistUnverifiedDevices", null, SettingLevel.DEVICE, checked);
+            }}
+        >
+            <span className="mx_OtherSettings_title">
+                {_t("settings|encryption|advanced|other_people_device_title")}
+            </span>
+            <InlineField
+                name="neverSendEncrypted"
+                control={<ToggleControl name="neverSendEncrypted" defaultChecked={blacklistUnverifiedDevices} />}
+            >
+                <Label>{_t("settings|encryption|advanced|other_people_device_label")}</Label>
+                <HelpMessage>{_t("settings|encryption|advanced|other_people_device_description")}</HelpMessage>
+            </InlineField>
+        </Root>
+    );
+}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 591b834be8f..6282397670e 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -2474,6 +2474,9 @@
                 "details_title": "Encryption details",
                 "export_keys": "Export keys",
                 "import_keys": "Import keys",
+                "other_people_device_description": "By default in encrypted rooms, do not send encrypted messages to anyone until you’ve verified them",
+                "other_people_device_label": "Never send encrypted messages to unverified devices",
+                "other_people_device_title": "Other people’s devices",
                 "reset_identity": "Reset cryptographic identity",
                 "session_id": "Session ID:",
                 "session_key": "Session key:",