From 75770f7a4a039c060a99681fc235f1ebd66737b2 Mon Sep 17 00:00:00 2001
From: David Baker <dave@matrix.org>
Date: Thu, 15 Aug 2019 12:04:07 +0100
Subject: [PATCH 1/5] Warn on disconnecting from IS

...if the user has bound threepids

Fixes https://github.com/vector-im/riot-web/issues/10550
---
 src/components/views/settings/SetIdServer.js  | 66 +++++++++++++------
 .../settings/discovery/EmailAddresses.js      | 24 +------
 .../views/settings/discovery/PhoneNumbers.js  | 24 +------
 src/i18n/strings/en_EN.json                   |  3 +-
 4 files changed, 53 insertions(+), 64 deletions(-)

diff --git a/src/components/views/settings/SetIdServer.js b/src/components/views/settings/SetIdServer.js
index 398e578e8d..472928f43b 100644
--- a/src/components/views/settings/SetIdServer.js
+++ b/src/components/views/settings/SetIdServer.js
@@ -22,6 +22,7 @@ import MatrixClientPeg from "../../../MatrixClientPeg";
 import SdkConfig from "../../../SdkConfig";
 import Modal from '../../../Modal';
 import dis from "../../../dispatcher";
+import { getThreepidBindStatus } from '../../../boundThreepids';
 
 /**
  * If a url has no path component, etc. abbreviate it to just the hostname
@@ -98,6 +99,7 @@ export default class SetIdServer extends React.Component {
             idServer: defaultIdServer,
             error: null,
             busy: false,
+            disconnectBusy: false,
         };
     }
 
@@ -150,24 +152,45 @@ export default class SetIdServer extends React.Component {
         });
     };
 
-    _onDisconnectClicked = () => {
-        const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
-        Modal.createTrackedDialog('Identity Server Disconnect Warning', '', QuestionDialog, {
-            title: _t("Disconnect Identity Server"),
-            description:
-                <div>
-                    {_t(
-                        "Disconnect from the identity server <idserver />?", {},
-                        {idserver: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>},
-                    )},
-                </div>,
-            button: _t("Disconnect"),
-            onFinished: (confirmed) => {
-                if (confirmed) {
-                    this._disconnectIdServer();
-                }
-            },
-        });
+    _onDisconnectClicked = async () => {
+        this.setState({disconnectBusy: true});
+        try {
+            const threepids = await getThreepidBindStatus(MatrixClientPeg.get());
+
+            const boundThreepids = threepids.filter(tp => tp.bound);
+            let message;
+            if (boundThreepids.length) {
+                message = _t(
+                    "You are currently sharing email addresses or phone numbers on the identity " +
+                    "server <idserver />. You will need to reconnect to <idserver2 /> to stop " +
+                    "sharing them.", {},
+                    {
+                        idserver: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>,
+                        // XXX: https://github.com/vector-im/riot-web/issues/10564
+                        idserver2: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>,
+                    }
+                );
+            } else {
+                message = _t(
+                    "Disconnect from the identity server <idserver />?", {},
+                    {idserver: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>},
+                );
+            }
+
+            const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
+            Modal.createTrackedDialog('Identity Server Disconnect Warning', '', QuestionDialog, {
+                title: _t("Disconnect Identity Server"),
+                description: message,
+                button: _t("Disconnect"),
+                onFinished: (confirmed) => {
+                    if (confirmed) {
+                        this._disconnectIdServer();
+                    }
+                },
+            });
+        } finally {
+            this.setState({disconnectBusy: false});
+        }
     };
 
     _disconnectIdServer = () => {
@@ -215,6 +238,11 @@ export default class SetIdServer extends React.Component {
 
         let discoSection;
         if (idServerUrl) {
+            let discoButtonContent = _t("Disconnect");
+            if (this.state.disconnectBusy) {
+                const InlineSpinner = sdk.getComponent('views.elements.InlineSpinner');
+                discoButtonContent = <InlineSpinner />;
+            }
             discoSection = <div>
                 <span className="mx_SettingsTab_subsectionText">{_t(
                     "Disconnecting from your identity server will mean you " +
@@ -222,7 +250,7 @@ export default class SetIdServer extends React.Component {
                     "able to invite others by email or phone.",
                 )}</span>
                 <AccessibleButton onClick={this._onDisconnectClicked} kind="danger">
-                    {_t("Disconnect")}
+                    {discoButtonContent}
                 </AccessibleButton>
             </div>;
         }
diff --git a/src/components/views/settings/discovery/EmailAddresses.js b/src/components/views/settings/discovery/EmailAddresses.js
index 7862eda61e..dd719bec5d 100644
--- a/src/components/views/settings/discovery/EmailAddresses.js
+++ b/src/components/views/settings/discovery/EmailAddresses.js
@@ -24,6 +24,7 @@ import sdk from '../../../../index';
 import Modal from '../../../../Modal';
 import IdentityAuthClient from '../../../../IdentityAuthClient';
 import AddThreepid from '../../../../AddThreepid';
+import { getThreepidBindStatus } from '../../../../boundThreepids';
 
 /*
 TODO: Improve the UX for everything in here.
@@ -201,28 +202,7 @@ export default class EmailAddresses extends React.Component {
         const userId = client.getUserId();
 
         const { threepids } = await client.getThreePids();
-        const emails = threepids.filter((a) => a.medium === 'email');
-
-        if (emails.length > 0) {
-            // TODO: Handle terms agreement
-            // See https://github.com/vector-im/riot-web/issues/10522
-            const authClient = new IdentityAuthClient();
-            const identityAccessToken = await authClient.getAccessToken();
-
-            // Restructure for lookup query
-            const query = emails.map(({ medium, address }) => [medium, address]);
-            const lookupResults = await client.bulkLookupThreePids(query, identityAccessToken);
-
-            // Record which are already bound
-            for (const [medium, address, mxid] of lookupResults.threepids) {
-                if (medium !== "email" || mxid !== userId) {
-                    continue;
-                }
-                const email = emails.find(e => e.address === address);
-                if (!email) continue;
-                email.bound = true;
-            }
-        }
+        const emails = await getThreepidBindStatus(client, 'email');
 
         this.setState({ emails });
     }
diff --git a/src/components/views/settings/discovery/PhoneNumbers.js b/src/components/views/settings/discovery/PhoneNumbers.js
index 3930277aea..97ca98a235 100644
--- a/src/components/views/settings/discovery/PhoneNumbers.js
+++ b/src/components/views/settings/discovery/PhoneNumbers.js
@@ -24,6 +24,7 @@ import sdk from '../../../../index';
 import Modal from '../../../../Modal';
 import IdentityAuthClient from '../../../../IdentityAuthClient';
 import AddThreepid from '../../../../AddThreepid';
+import { getThreepidBindStatus } from '../../../../boundThreepids';
 
 /*
 TODO: Improve the UX for everything in here.
@@ -220,28 +221,7 @@ export default class PhoneNumbers extends React.Component {
         const userId = client.getUserId();
 
         const { threepids } = await client.getThreePids();
-        const msisdns = threepids.filter((a) => a.medium === 'msisdn');
-
-        if (msisdns.length > 0) {
-            // TODO: Handle terms agreement
-            // See https://github.com/vector-im/riot-web/issues/10522
-            const authClient = new IdentityAuthClient();
-            const identityAccessToken = await authClient.getAccessToken();
-
-            // Restructure for lookup query
-            const query = msisdns.map(({ medium, address }) => [medium, address]);
-            const lookupResults = await client.bulkLookupThreePids(query, identityAccessToken);
-
-            // Record which are already bound
-            for (const [medium, address, mxid] of lookupResults.threepids) {
-                if (medium !== "msisdn" || mxid !== userId) {
-                    continue;
-                }
-                const msisdn = msisdns.find(e => e.address === address);
-                if (!msisdn) continue;
-                msisdn.bound = true;
-            }
-        }
+        const msisdns = await getThreepidBindStatus(client, 'msisdn');
 
         this.setState({ msisdns });
     }
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index e5ecc2bf19..89a12f05a5 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -543,8 +543,9 @@
     "Not a valid Identity Server (status code %(code)s)": "Not a valid Identity Server (status code %(code)s)",
     "Could not connect to Identity Server": "Could not connect to Identity Server",
     "Checking server": "Checking server",
-    "Disconnect Identity Server": "Disconnect Identity Server",
+    "You are currently sharing email addresses or phone numbers on the identity server <idserver />. You will need to reconnect to <idserver2 /> to stop sharing them.": "You are currently sharing email addresses or phone numbers on the identity server <idserver />. You will need to reconnect to <idserver2 /> to stop sharing them.",
     "Disconnect from the identity server <idserver />?": "Disconnect from the identity server <idserver />?",
+    "Disconnect Identity Server": "Disconnect Identity Server",
     "Disconnect": "Disconnect",
     "Identity Server (%(server)s)": "Identity Server (%(server)s)",
     "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.": "You are currently using <server></server> to discover and be discoverable by existing contacts you know. You can change your identity server below.",

From 54fb1b5302565fbee094b35718e7dc4dd6a5aa62 Mon Sep 17 00:00:00 2001
From: David Baker <dave@matrix.org>
Date: Thu, 15 Aug 2019 12:07:59 +0100
Subject: [PATCH 2/5] Unused variables / imports

---
 src/components/views/settings/discovery/EmailAddresses.js | 3 ---
 src/components/views/settings/discovery/PhoneNumbers.js   | 3 ---
 2 files changed, 6 deletions(-)

diff --git a/src/components/views/settings/discovery/EmailAddresses.js b/src/components/views/settings/discovery/EmailAddresses.js
index dd719bec5d..4d18c1d355 100644
--- a/src/components/views/settings/discovery/EmailAddresses.js
+++ b/src/components/views/settings/discovery/EmailAddresses.js
@@ -22,7 +22,6 @@ import { _t } from "../../../../languageHandler";
 import MatrixClientPeg from "../../../../MatrixClientPeg";
 import sdk from '../../../../index';
 import Modal from '../../../../Modal';
-import IdentityAuthClient from '../../../../IdentityAuthClient';
 import AddThreepid from '../../../../AddThreepid';
 import { getThreepidBindStatus } from '../../../../boundThreepids';
 
@@ -199,9 +198,7 @@ export default class EmailAddresses extends React.Component {
 
     async componentWillMount() {
         const client = MatrixClientPeg.get();
-        const userId = client.getUserId();
 
-        const { threepids } = await client.getThreePids();
         const emails = await getThreepidBindStatus(client, 'email');
 
         this.setState({ emails });
diff --git a/src/components/views/settings/discovery/PhoneNumbers.js b/src/components/views/settings/discovery/PhoneNumbers.js
index 97ca98a235..fdebac5d22 100644
--- a/src/components/views/settings/discovery/PhoneNumbers.js
+++ b/src/components/views/settings/discovery/PhoneNumbers.js
@@ -22,7 +22,6 @@ import { _t } from "../../../../languageHandler";
 import MatrixClientPeg from "../../../../MatrixClientPeg";
 import sdk from '../../../../index';
 import Modal from '../../../../Modal';
-import IdentityAuthClient from '../../../../IdentityAuthClient';
 import AddThreepid from '../../../../AddThreepid';
 import { getThreepidBindStatus } from '../../../../boundThreepids';
 
@@ -218,9 +217,7 @@ export default class PhoneNumbers extends React.Component {
 
     async componentWillMount() {
         const client = MatrixClientPeg.get();
-        const userId = client.getUserId();
 
-        const { threepids } = await client.getThreePids();
         const msisdns = await getThreepidBindStatus(client, 'msisdn');
 
         this.setState({ msisdns });

From e6c5775d0efc21f887f562f041e57faefb92ce8a Mon Sep 17 00:00:00 2001
From: David Baker <dave@matrix.org>
Date: Thu, 15 Aug 2019 12:16:59 +0100
Subject: [PATCH 3/5] Commit the new file

---
 src/boundThreepids.js | 52 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)
 create mode 100644 src/boundThreepids.js

diff --git a/src/boundThreepids.js b/src/boundThreepids.js
new file mode 100644
index 0000000000..799728f801
--- /dev/null
+++ b/src/boundThreepids.js
@@ -0,0 +1,52 @@
+/*
+Copyright 2019 The Matrix.org Foundation C.I.C.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+import IdentityAuthClient from './IdentityAuthClient';
+
+export async function getThreepidBindStatus(client, filterMedium) {
+    const userId = client.getUserId();
+
+    let { threepids } = await client.getThreePids();
+    if (filterMedium) {
+        threepids = threepids.filter((a) => a.medium === filterMedium);
+    }
+
+    if (threepids.length > 0) {
+        // TODO: Handle terms agreement
+        // See https://github.com/vector-im/riot-web/issues/10522
+        const authClient = new IdentityAuthClient();
+        const identityAccessToken = await authClient.getAccessToken();
+
+        // Restructure for lookup query
+        const query = threepids.map(({ medium, address }) => [medium, address]);
+        const lookupResults = await client.bulkLookupThreePids(query, identityAccessToken);
+
+        // Record which are already bound
+        for (const [medium, address, mxid] of lookupResults.threepids) {
+            if (mxid !== userId) {
+                continue;
+            }
+            if (filterMedium && medium !== filterMedium) {
+                continue;
+            }
+            const threepid = threepids.find(e => e.medium === medium && e.address === address);
+            if (!threepid) continue;
+            threepid.bound = true;
+        }
+    }
+
+    return threepids;
+}

From 6f07f9157c733b9c203c1b35b78f162384e3bd6e Mon Sep 17 00:00:00 2001
From: David Baker <dave@matrix.org>
Date: Thu, 15 Aug 2019 13:10:05 +0100
Subject: [PATCH 4/5] lint

---
 src/components/views/settings/SetIdServer.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/views/settings/SetIdServer.js b/src/components/views/settings/SetIdServer.js
index 472928f43b..3a67d124ae 100644
--- a/src/components/views/settings/SetIdServer.js
+++ b/src/components/views/settings/SetIdServer.js
@@ -168,7 +168,7 @@ export default class SetIdServer extends React.Component {
                         idserver: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>,
                         // XXX: https://github.com/vector-im/riot-web/issues/10564
                         idserver2: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>,
-                    }
+                    },
                 );
             } else {
                 message = _t(

From 7d96cc969ada88e94147422707ce111f7b95428b Mon Sep 17 00:00:00 2001
From: David Baker <dbkr@users.noreply.github.com>
Date: Thu, 15 Aug 2019 15:39:15 +0100
Subject: [PATCH 5/5] use original bug

Co-Authored-By: J. Ryan Stinnett <jryans@gmail.com>
---
 src/components/views/settings/SetIdServer.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/components/views/settings/SetIdServer.js b/src/components/views/settings/SetIdServer.js
index 3a67d124ae..c0d103a219 100644
--- a/src/components/views/settings/SetIdServer.js
+++ b/src/components/views/settings/SetIdServer.js
@@ -166,7 +166,7 @@ export default class SetIdServer extends React.Component {
                     "sharing them.", {},
                     {
                         idserver: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>,
-                        // XXX: https://github.com/vector-im/riot-web/issues/10564
+                        // XXX: https://github.com/vector-im/riot-web/issues/9086
                         idserver2: sub => <b>{abbreviateUrl(this.state.currentClientIdServer)}</b>,
                     },
                 );