From 07f4fe7bee862129574bae0b023958d9242669fd Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Tue, 9 Apr 2024 16:05:53 +0200
Subject: [PATCH 01/15] Clean up isAccountExpired
---
src/tchap/util/TchapUtils.ts | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/tchap/util/TchapUtils.ts b/src/tchap/util/TchapUtils.ts
index 2b0c186bc..4c4a400d9 100644
--- a/src/tchap/util/TchapUtils.ts
+++ b/src/tchap/util/TchapUtils.ts
@@ -6,6 +6,7 @@ import { findMapStyleUrl } from "matrix-react-sdk/src/utils/location";
import TchapApi from "./TchapApi";
import { ClientConfig } from "~tchap-web/yarn-linked-dependencies/matrix-js-sdk/src/autodiscovery";
+import { MatrixError } from "~tchap-web/yarn-linked-dependencies/matrix-js-sdk/src/http-api";
/**
* Tchap utils.
@@ -220,19 +221,20 @@ export default class TchapUtils {
}
/**
- * Verify if the account is expired.
- * It executes an API call and check that it receives a ORG_MATRIX_EXPIRED_ACCOUNT
- * The API invoked is getProfileInfo()
- * @param matrixId the account matrix Id
+ * Verify if the currently logged in account is expired.
+ * It executes an API call (to getProfileInfo) and checks whether the call throws a ORG_MATRIX_EXPIRED_ACCOUNT
* @returns true if account is expired, false otherwise
*/
- static async isAccountExpired(matrixId?: string): Promise {
+ static async isAccountExpired(): Promise {
+ const client = MatrixClientPeg.safeGet(); // todo(estelle) throws if client is not logged in. Is that what we want ?
+ const matrixId: string | null = client.credentials.userId;
if (!matrixId) {
- matrixId = MatrixClientPeg.getCredentials().userId;
+ // throw ? return ? todo(estelle)
}
try {
- await MatrixClientPeg.get().getProfileInfo(matrixId);
- } catch (err) {
+ await client.getProfileInfo(matrixId!);
+ } catch (error) {
+ const err = error as MatrixError;
if (err.errcode === "ORG_MATRIX_EXPIRED_ACCOUNT") {
return true;
}
From a9d226baeff60d1775a7842edbdae4b24e417ab9 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Tue, 9 Apr 2024 16:27:22 +0200
Subject: [PATCH 02/15] Update more code
---
src/tchap/util/TchapUtils.ts | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/src/tchap/util/TchapUtils.ts b/src/tchap/util/TchapUtils.ts
index 4c4a400d9..c698c1a25 100644
--- a/src/tchap/util/TchapUtils.ts
+++ b/src/tchap/util/TchapUtils.ts
@@ -157,8 +157,9 @@ export default class TchapUtils {
* @returns true is a map tile server is present in config or wellknown.
*/
static isMapConfigured(): boolean {
+ const cli = MatrixClientPeg.get();
try {
- findMapStyleUrl();
+ findMapStyleUrl(cli);
return true;
} catch (e) {
return false;
@@ -189,6 +190,8 @@ export default class TchapUtils {
const k = keys[i];
url += k + "=" + encodeURIComponent(params[k]);
}
+ // todo(estelle) : write unit test, then try replacing by :
+ // url += "?" + new URLSearchParams(params).toString();
return url;
}
@@ -197,9 +200,10 @@ export default class TchapUtils {
* @returns true if the mail was sent succesfully, false otherwise
*/
static async requestNewExpiredAccountEmail(): Promise {
- console.log(":tchap: Requesting an email to renew to account");
- const homeserverUrl = MatrixClientPeg.get().getHomeserverUrl();
- const accessToken = MatrixClientPeg.get().getAccessToken();
+ console.debug(":tchap: Requesting an email to renew to account"); // todo(estelle) reuse logger class
+ const client = MatrixClientPeg.get(); // todo(estelle) what to do if client is null ? use safeGet or get ?
+ const homeserverUrl = client.getHomeserverUrl();
+ const accessToken = client.getAccessToken();
//const url = `${homeserverUrl}/_matrix/client/unstable/account_validity/send_mail`;
const url = `${homeserverUrl}${TchapApi.accountValidityResendEmailUrl}`;
const options = {
From 6c5d6cb1d1e64242b789f0321cec76785c2f2162 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Tue, 9 Apr 2024 16:39:11 +0200
Subject: [PATCH 03/15] Small cleanups and todos
---
.../views/dialogs/ExpiredAccountDialog.tsx | 4 ++--
src/tchap/lib/ExpiredAccountHandler.ts | 19 ++++++++++---------
2 files changed, 12 insertions(+), 11 deletions(-)
diff --git a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
index 4f76c6b13..b87744656 100644
--- a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
+++ b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
@@ -9,7 +9,7 @@ import TchapUtils from "../../../util/TchapUtils";
interface IProps {
onFinished(): void;
onRequestNewEmail(): Promise;
- emailDelay?: number; //delay betwenn 2 emails in seconds, by default 30
+ emailDelay?: number; //delay between 2 emails in seconds, by default 30
}
interface IState {
@@ -33,7 +33,7 @@ enum ProcessState {
*/
export default class ExpiredAccountDialog extends React.Component {
- constructor(props) {
+ constructor(props: IProps) {
super(props);
this.state = {
isAccountExpired: false,
diff --git a/src/tchap/lib/ExpiredAccountHandler.ts b/src/tchap/lib/ExpiredAccountHandler.ts
index a2cc27154..c03dc71a8 100644
--- a/src/tchap/lib/ExpiredAccountHandler.ts
+++ b/src/tchap/lib/ExpiredAccountHandler.ts
@@ -28,12 +28,13 @@ class ExpiredAccountHandler {
}
/**
- * register the listener after the Matrix Client has been initialized but before it is started
+ * Register to listen to expired account event.
+ * Registration is done after the Matrix Client has been initialized but before it is started.
*/
public register() {
const expiredRegistrationId = this.dispatcher.register((payload: ActionPayload) => {
if (payload.action === "will_start_client") {
- console.log(":tchap: register a listener for HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT events");
+ console.log(":tchap: register a listener for HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT events"); // todo(estelle) logger
const cli = MatrixClientPeg.get();
cli.on(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT, this.boundOnExpiredAccountEvent);
//unregister callback once the work is done
@@ -46,14 +47,14 @@ class ExpiredAccountHandler {
* When account expired account happens, display the panel if not open yet.
*/
private onExpiredAccountError() {
- console.log(":tchap: Expired Account Error received");
+ console.log(":tchap: Expired Account Error received"); // todo(estelle) reuse logger class
if (this.isPanelOpen) {
return;
}
//shutdown all matrix react services, but without unsetting the client
stopMatrixClient(false);
- console.log(":tchap: matrix react services have been shutdown");
+ console.log(":tchap: matrix react services have been shutdown"); // todo(estelle) reuse logger class
//should we sent the email directly? Normally they should have received already an email 7 days earlier
this.showExpirationPanel();
@@ -63,7 +64,7 @@ class ExpiredAccountHandler {
private async showExpirationPanel() {
Modal.createDialog(
ExpiredAccountDialog,
- {
+ { /* props */
onRequestNewEmail: () => {
return TchapUtils.requestNewExpiredAccountEmail();
},
@@ -74,10 +75,10 @@ class ExpiredAccountHandler {
},
//todo: define which static/dynamic settings are needed for this dialog
},
- null,
- false,
- true,
- {
+ undefined /* className */,
+ false /* isPriorityModal */,
+ true /* isStaticModal */,
+ { /* options */
//close panel only if account is not expired
onBeforeClose: async () => {
//verify that the account is not expired anymore
From c5bc61c090902c1c0868ba2d449facab5474c8e7 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Tue, 9 Apr 2024 16:44:11 +0200
Subject: [PATCH 04/15] More light cleanup
---
src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
index b87744656..868288c65 100644
--- a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
+++ b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
@@ -27,10 +27,9 @@ enum ProcessState {
ACCOUNT_RENEWED,
}
/**
- * Expired Account is displayed when the user account is expired. It can not be cancel until the account is renewed.
+ * ExpiredAccountDialog is displayed when the user account is expired. It can not be canceled until the account is renewed.
* This panel is exclusively opened by the listener ExpiredAccountHandler
* This component is required when activating the plugin synapse-email-account-validity on the server side: https://github.com/matrix-org/synapse-email-account-validity
-
*/
export default class ExpiredAccountDialog extends React.Component {
constructor(props: IProps) {
From edc83a14977d6b71b6cda11390a8ab0dbd8c4cee Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Tue, 9 Apr 2024 17:03:41 +0200
Subject: [PATCH 05/15] Tiny translation fixes
---
modules/tchap-translations/tchap_translations.json | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/modules/tchap-translations/tchap_translations.json b/modules/tchap-translations/tchap_translations.json
index 6240d3dff..93f87936d 100644
--- a/modules/tchap-translations/tchap_translations.json
+++ b/modules/tchap-translations/tchap_translations.json
@@ -301,7 +301,7 @@
"Verified!": { "en": "Verified!", "fr": "Vérifié !" },
"Wait for at least %(wait)s seconds between two emails": {
"en": "Wait for at least %(wait)s seconds between two emails",
- "fr": "Attendez au moins %(wait)s secondes entre l'envoi de deux mails"
+ "fr": "Attendez au moins %(wait)s secondes entre l'envoi de deux emails"
},
"Warning: this is the only time this code will be displayed!": {
"en": "Warning: this is the only time this code will be displayed!",
@@ -322,8 +322,8 @@
"fr": "Vos Clés Tchap (clés de chiffrement) ont été sauvegardées avec succès dans le fichier tchap-keys.txt. Vous pourrez les importer à votre prochaine connexion pour déverrouiller vos messages (en savoir plus)."
},
"Your account is still expired, please follow the link in the email you have received to renew it": {
- "en": "Your account is still expired, please follow the link in the email you should have received to renew it",
- "fr": "Votre compte est toujours expiré, merci de cliquer sur le lien reçu dans le mail de renouvellement"
+ "en": "Your account is still expired, please follow the link in the email you should have received to renew it.",
+ "fr": "Votre compte est toujours expiré, merci de cliquer sur le lien reçu dans le mail de renouvellement."
},
"Your last three login attempts have failed. Please try again in a few minutes.": {
"en": "Your last three login attempts have failed. Please try again in a few minutes.",
From 4b693f77a73af9681f5ca02ee277c9369e9d9b00 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Tue, 9 Apr 2024 17:25:42 +0200
Subject: [PATCH 06/15] Use safeGet everywhere, so that it will throw if client
is not initialised.
---
src/tchap/lib/ExpiredAccountHandler.ts | 3 ++-
src/tchap/util/TchapUtils.ts | 17 ++++++++++-------
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/tchap/lib/ExpiredAccountHandler.ts b/src/tchap/lib/ExpiredAccountHandler.ts
index c03dc71a8..0745c30b3 100644
--- a/src/tchap/lib/ExpiredAccountHandler.ts
+++ b/src/tchap/lib/ExpiredAccountHandler.ts
@@ -35,7 +35,8 @@ class ExpiredAccountHandler {
const expiredRegistrationId = this.dispatcher.register((payload: ActionPayload) => {
if (payload.action === "will_start_client") {
console.log(":tchap: register a listener for HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT events"); // todo(estelle) logger
- const cli = MatrixClientPeg.get();
+ // safeGet will throw if client is not initialised yet. We don't handle it because we don't know when it would happen.
+ const cli = MatrixClientPeg.safeGet();
cli.on(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT, this.boundOnExpiredAccountEvent);
//unregister callback once the work is done
this.dispatcher.unregister(expiredRegistrationId);
diff --git a/src/tchap/util/TchapUtils.ts b/src/tchap/util/TchapUtils.ts
index c698c1a25..8580eb3c4 100644
--- a/src/tchap/util/TchapUtils.ts
+++ b/src/tchap/util/TchapUtils.ts
@@ -18,9 +18,9 @@ export default class TchapUtils {
* @returns {string} The shortened value of getDomain().
*/
static getShortDomain(): string {
- const cli = MatrixClientPeg.get();
+ const cli = MatrixClientPeg.safeGet();
const baseDomain = cli.getDomain();
- const domain = baseDomain.split(".tchap.gouv.fr")[0].split(".").reverse().filter(Boolean)[0];
+ const domain = baseDomain?.split(".tchap.gouv.fr")[0].split(".").reverse().filter(Boolean)[0];
return this.capitalize(domain) || "Tchap";
}
@@ -34,7 +34,7 @@ export default class TchapUtils {
showForumFederationSwitch: boolean;
forumFederationSwitchDefaultValue?: boolean;
} {
- const cli = MatrixClientPeg.get();
+ const cli = MatrixClientPeg.safeGet();
const baseDomain = cli.getDomain();
// Only show the federate switch to defense users : it's difficult to understand, so we avoid
@@ -148,7 +148,7 @@ export default class TchapUtils {
* @returns Promise is cross signing is supported by home server or false
*/
static async isCrossSigningSupportedByServer(): Promise {
- const cli = MatrixClientPeg.get();
+ const cli = MatrixClientPeg.safeGet();
return cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
}
@@ -157,7 +157,7 @@ export default class TchapUtils {
* @returns true is a map tile server is present in config or wellknown.
*/
static isMapConfigured(): boolean {
- const cli = MatrixClientPeg.get();
+ const cli = MatrixClientPeg.safeGet();
try {
findMapStyleUrl(cli);
return true;
@@ -201,7 +201,10 @@ export default class TchapUtils {
*/
static async requestNewExpiredAccountEmail(): Promise {
console.debug(":tchap: Requesting an email to renew to account"); // todo(estelle) reuse logger class
- const client = MatrixClientPeg.get(); // todo(estelle) what to do if client is null ? use safeGet or get ?
+
+ // safeGet will throw if client is not initialised. We don't handle it because we don't know when this would happen.
+ const client = MatrixClientPeg.safeGet();
+
const homeserverUrl = client.getHomeserverUrl();
const accessToken = client.getAccessToken();
//const url = `${homeserverUrl}/_matrix/client/unstable/account_validity/send_mail`;
@@ -230,7 +233,7 @@ export default class TchapUtils {
* @returns true if account is expired, false otherwise
*/
static async isAccountExpired(): Promise {
- const client = MatrixClientPeg.safeGet(); // todo(estelle) throws if client is not logged in. Is that what we want ?
+ const client = MatrixClientPeg.safeGet();
const matrixId: string | null = client.credentials.userId;
if (!matrixId) {
// throw ? return ? todo(estelle)
From 5089ffcfb3b3b89aad6ac15d6265f75af62380a5 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 14:00:53 +0200
Subject: [PATCH 07/15] Add a wait spinner when sending email
---
.../views/dialogs/ExpiredAccountDialog.tsx | 21 +++++++++++++------
src/tchap/util/TchapUtils.ts | 3 ++-
2 files changed, 17 insertions(+), 7 deletions(-)
diff --git a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
index 868288c65..53dccd7fa 100644
--- a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
+++ b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
@@ -3,23 +3,26 @@ import React from "react";
import { _t } from "matrix-react-sdk/src/languageHandler";
import BaseDialog from "matrix-react-sdk/src/components/views/dialogs/BaseDialog";
import DialogButtons from "matrix-react-sdk/src/components/views/elements/DialogButtons";
+import InlineSpinner from "matrix-react-sdk/src/components/views/elements/InlineSpinner";
import TchapUtils from "../../../util/TchapUtils";
interface IProps {
onFinished(): void;
onRequestNewEmail(): Promise;
- emailDelay?: number; //delay between 2 emails in seconds, by default 30
+ emailDelaySecs?: number; //delay between 2 emails in seconds, by default 30
}
interface IState {
- emailDelay: number; //delay betwenn 2 emails in seconds, by default 30
+ emailDelaySecs: number; //delay betwenn 2 emails in seconds, by default 30
isAccountExpired: boolean; //todo: not used yet
newEmailSentTimestamp: number; //timestamp
ProcessState: ProcessState;
}
enum ProcessState {
+ START,
+ SENDING_EMAIL,
EMAIL_MUST_WAIT,
EMAIL_SUCCESS,
EMAIL_FAILURE,
@@ -37,8 +40,8 @@ export default class ExpiredAccountDialog extends React.Component {
this.setState({
newEmailSentTimestamp: success ? Date.now() : this.state.newEmailSentTimestamp,
@@ -94,12 +100,15 @@ export default class ExpiredAccountDialog extends React.Component // todo translation+format or spinner
+ break;
case ProcessState.EMAIL_MUST_WAIT:
//don't know which class should decorate this message, it is not really an error
//its goal is to avoid users to click twice or more on the button and spam themselves
alertMessage = (
- {_t("Wait for at least %(wait)s seconds between two emails", { wait: this.state.emailDelay })}
+ {_t("Wait for at least %(wait)s seconds between two emails", { wait: this.state.emailDelaySecs })}
);
break;
diff --git a/src/tchap/util/TchapUtils.ts b/src/tchap/util/TchapUtils.ts
index 8580eb3c4..9e2e571fe 100644
--- a/src/tchap/util/TchapUtils.ts
+++ b/src/tchap/util/TchapUtils.ts
@@ -236,7 +236,8 @@ export default class TchapUtils {
const client = MatrixClientPeg.safeGet();
const matrixId: string | null = client.credentials.userId;
if (!matrixId) {
- // throw ? return ? todo(estelle)
+ // user is not logged in. Or something went wrong.
+ return false;
}
try {
await client.getProfileInfo(matrixId!);
From 2528da17ae4156db193e18f747fcbbbb0a46ed0e Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 14:16:31 +0200
Subject: [PATCH 08/15] User proper logger
---
src/tchap/lib/ExpiredAccountHandler.ts | 7 ++++---
src/tchap/util/TchapUtils.ts | 15 ++++++++-------
2 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/src/tchap/lib/ExpiredAccountHandler.ts b/src/tchap/lib/ExpiredAccountHandler.ts
index 0745c30b3..2ec64e454 100644
--- a/src/tchap/lib/ExpiredAccountHandler.ts
+++ b/src/tchap/lib/ExpiredAccountHandler.ts
@@ -5,6 +5,7 @@ import { stopMatrixClient } from "matrix-react-sdk/src/Lifecycle";
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
import Modal from "matrix-react-sdk/src/Modal";
import PlatformPeg from "matrix-react-sdk/src/PlatformPeg";
+import { logger } from "matrix-js-sdk/src/logger";
import ExpiredAccountDialog from "../components/views/dialogs/ExpiredAccountDialog";
import TchapUtils from "../util/TchapUtils";
@@ -34,7 +35,7 @@ class ExpiredAccountHandler {
public register() {
const expiredRegistrationId = this.dispatcher.register((payload: ActionPayload) => {
if (payload.action === "will_start_client") {
- console.log(":tchap: register a listener for HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT events"); // todo(estelle) logger
+ logger.debug(":tchap: register a listener for HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT events");
// safeGet will throw if client is not initialised yet. We don't handle it because we don't know when it would happen.
const cli = MatrixClientPeg.safeGet();
cli.on(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT, this.boundOnExpiredAccountEvent);
@@ -48,14 +49,14 @@ class ExpiredAccountHandler {
* When account expired account happens, display the panel if not open yet.
*/
private onExpiredAccountError() {
- console.log(":tchap: Expired Account Error received"); // todo(estelle) reuse logger class
+ logger.debug(":tchap: Expired Account Error received");
if (this.isPanelOpen) {
return;
}
//shutdown all matrix react services, but without unsetting the client
stopMatrixClient(false);
- console.log(":tchap: matrix react services have been shutdown"); // todo(estelle) reuse logger class
+ logger.debug(":tchap: matrix react services have been shutdown");
//should we sent the email directly? Normally they should have received already an email 7 days earlier
this.showExpirationPanel();
diff --git a/src/tchap/util/TchapUtils.ts b/src/tchap/util/TchapUtils.ts
index 9e2e571fe..4585124d8 100644
--- a/src/tchap/util/TchapUtils.ts
+++ b/src/tchap/util/TchapUtils.ts
@@ -3,6 +3,7 @@ import SdkConfig from "matrix-react-sdk/src/SdkConfig";
import AutoDiscoveryUtils from "matrix-react-sdk/src/utils/AutoDiscoveryUtils";
import { ValidatedServerConfig } from "matrix-react-sdk/src/utils/ValidatedServerConfig";
import { findMapStyleUrl } from "matrix-react-sdk/src/utils/location";
+import { logger } from "matrix-js-sdk/src/logger";
import TchapApi from "./TchapApi";
import { ClientConfig } from "~tchap-web/yarn-linked-dependencies/matrix-js-sdk/src/autodiscovery";
@@ -98,7 +99,7 @@ export default class TchapUtils {
};
})
.catch((error) => {
- console.error("Could not find homeserver for this email", error);
+ logger.error("Could not find homeserver for this email", error);
return;
});
};
@@ -200,7 +201,7 @@ export default class TchapUtils {
* @returns true if the mail was sent succesfully, false otherwise
*/
static async requestNewExpiredAccountEmail(): Promise {
- console.debug(":tchap: Requesting an email to renew to account"); // todo(estelle) reuse logger class
+ logger.debug(":tchap: Requesting an email to renew to account");
// safeGet will throw if client is not initialised. We don't handle it because we don't know when this would happen.
const client = MatrixClientPeg.safeGet();
@@ -218,11 +219,11 @@ export default class TchapUtils {
return fetch(url, options)
.then((response) => {
- console.log(":tchap: email NewExpiredAccountEmail sent", response);
+ logger.debug(":tchap: email NewExpiredAccountEmail sent", response);
return true;
})
.catch((err) => {
- console.error(":tchap: email NewExpiredAccountEmail error", err);
+ logger.error(":tchap: email NewExpiredAccountEmail error", err);
return false;
});
}
@@ -257,7 +258,7 @@ export default class TchapUtils {
*/
static async isCurrentlyUsingBluetooth(): Promise {
if (!navigator.mediaDevices?.enumerateDevices) {
- console.log("enumerateDevices() not supported. Cannot know if there is a bluetooth device.");
+ logger.warn("enumerateDevices() not supported. Cannot know if there is a bluetooth device.");
return false;
} else {
// List cameras and microphones.
@@ -266,7 +267,7 @@ export default class TchapUtils {
.then((devices) => {
let hasBluetooth = false;
devices.forEach((device) => {
- console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
+ logger.debug(`${device.kind}: ${device.label} id = ${device.deviceId}`);
if (device.kind === "audioinput") {
if (device.label.toLowerCase().includes("bluetooth")) {
hasBluetooth = true;
@@ -276,7 +277,7 @@ export default class TchapUtils {
return hasBluetooth;
})
.catch((err) => {
- console.error(`${err.name}: ${err.message}`);
+ logger.error(`${err.name}: ${err.message}`);
return false;
});
}
From 15e2fb98de35c06166c2f9b9e5bb6f76595e3f09 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 14:30:03 +0200
Subject: [PATCH 09/15] Test drafts
---
.../components/views/dialogs/ExpiredAccountDialog.tsx | 2 +-
.../views/dialogs/ExpiredAccountDialog-test.tsx | 5 +++++
test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts | 9 +++++++++
3 files changed, 15 insertions(+), 1 deletion(-)
create mode 100644 test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
create mode 100644 test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
diff --git a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
index 53dccd7fa..a71257092 100644
--- a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
+++ b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
@@ -101,7 +101,7 @@ export default class ExpiredAccountDialog extends React.Component // todo translation+format or spinner
+ alertMessage =
break;
case ProcessState.EMAIL_MUST_WAIT:
//don't know which class should decorate this message, it is not really an error
diff --git a/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
new file mode 100644
index 000000000..3bdec6338
--- /dev/null
+++ b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
@@ -0,0 +1,5 @@
+describe("ExpiredAccountDialog", () => {
+ it("sends email when 'Send email' button is clicked", () => {});
+ it("when 'I renewed' is clicked, and the account is renewed, it displays 'cool' and unblocks user (todo)", () => {});
+ it("when 'I renewed' is clicked, and the account is not renewed, it displays 'not cool' and keeps dialog up (todo)", () => {});
+});
diff --git a/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts b/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
new file mode 100644
index 000000000..4d18fa82f
--- /dev/null
+++ b/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
@@ -0,0 +1,9 @@
+describe("ExpiredAccountHandler", () => {
+ it("displays dialog when account is expired", () => {
+ // Modal.createDialog
+ // create handler
+ // dispatch action : will_start_client
+ // cli.on(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT)
+ // expect Modal.createDialog to have been called, with ExpiredAccountDialog class
+ });
+});
From 105ba3f0263e40d84a9ae27c5ea5de39875c2531 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 15:00:09 +0200
Subject: [PATCH 10/15] Implment ExpiredAccountHandler-test
---
.../tchap/lib/ExpiredAccountHandler-test.ts | 51 +++++++++++++++++--
1 file changed, 47 insertions(+), 4 deletions(-)
diff --git a/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts b/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
index 4d18fa82f..f1c763608 100644
--- a/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
+++ b/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
@@ -1,9 +1,52 @@
+import { mocked, MockedObject } from "jest-mock";
+
+import ExpiredAccountDialog from "~tchap-web/src/tchap/components/views/dialogs/ExpiredAccountDialog";
+import ExpiredAccountHandler from "~tchap-web/src/tchap/lib/ExpiredAccountHandler";
+import { HttpApiEvent, MatrixClient } from "~tchap-web/yarn-linked-dependencies/matrix-js-sdk/src/matrix";
+import Modal from "~tchap-web/yarn-linked-dependencies/matrix-react-sdk/src/Modal";
+import defaultDispatcher from "~tchap-web/yarn-linked-dependencies/matrix-react-sdk/src/dispatcher/dispatcher";
+import { getMockClientWithEventEmitter } from "~tchap-web/yarn-linked-dependencies/matrix-react-sdk/test/test-utils";
+
describe("ExpiredAccountHandler", () => {
+ let mockClient: MockedObject;
+
+ beforeEach(() => {
+ Modal.createDialog = jest.fn();
+ // @ts-ignore mock (type error because empty return)
+ mocked(Modal.createDialog).mockReturnValue({});
+
+ mockClient = getMockClientWithEventEmitter({
+ stopClient: jest.fn(),
+ removeAllListeners: jest.fn(),
+ });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
it("displays dialog when account is expired", () => {
- // Modal.createDialog
- // create handler
- // dispatch action : will_start_client
+ // handler instance is created when import ExpiredAccountHandler is run.
+ ExpiredAccountHandler.register();
+
+ // Simulate start of app, for handler to initialise
+ defaultDispatcher.dispatch({ action: "will_start_client" }, true);
+
+ // Simulate expired error
// cli.on(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT)
- // expect Modal.createDialog to have been called, with ExpiredAccountDialog class
+ // this.eventEmitter.emit(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT, err);
+ mockClient.emit(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT);
+
+ expect(Modal.createDialog).toHaveBeenCalledWith(
+ ExpiredAccountDialog,
+ {
+ onRequestNewEmail: expect.any(Function),
+ onFinished: expect.any(Function),
+ },
+ undefined /* className */,
+ false /* isPriorityModal */,
+ true /* isStaticModal */,
+ { onBeforeClose: expect.any(Function) },
+ );
});
});
From afd702645e5ad73e9508f4a91b54b6d7cafe3cf9 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 15:42:24 +0200
Subject: [PATCH 11/15] More tests for dialog
---
.../views/dialogs/ExpiredAccountDialog.tsx | 8 +--
.../dialogs/ExpiredAccountDialog-test.tsx | 62 ++++++++++++++++++-
2 files changed, 65 insertions(+), 5 deletions(-)
diff --git a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
index a71257092..ad174e7f2 100644
--- a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
+++ b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
@@ -96,7 +96,7 @@ export default class ExpiredAccountDialog extends React.Component{_t("An email has been sent to you. Click on the link it contains, click below.")}
);
let alertMessage = null;
- let requestNewEmailButton = ;
+ let requestNewEmailButton = ;
let okButtonText = _t("I renewed the validity of my account");
switch (this.state.ProcessState) {
@@ -107,18 +107,18 @@ export default class ExpiredAccountDialog extends React.Component
+
{_t("Wait for at least %(wait)s seconds between two emails", { wait: this.state.emailDelaySecs })}
);
break;
case ProcessState.EMAIL_FAILURE:
alertMessage = (
-
{_t("The email was not sent sucessfully, please retry in a moment")}
+
{_t("The email was not sent sucessfully, please retry in a moment")}
);
break;
case ProcessState.EMAIL_SUCCESS:
- alertMessage =
{_t("A new email has been sent")}
;
+ alertMessage =
{_t("A new email has been sent")}
;
break;
case ProcessState.ACCOUNT_STILL_EXPIRED:
alertMessage = (
diff --git a/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
index 3bdec6338..ed592d46a 100644
--- a/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
+++ b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
@@ -1,5 +1,65 @@
+import * as React from "react";
+import { render, screen } from "@testing-library/react";
+
+import ExpiredAccountDialog from "~tchap-web/src/tchap/components/views/dialogs/ExpiredAccountDialog";
+import { flushPromises } from "~tchap-web/yarn-linked-dependencies/matrix-react-sdk/test/test-utils";
+
describe("ExpiredAccountDialog", () => {
- it("sends email when 'Send email' button is clicked", () => {});
+ describe("'Send email' button", () => {
+ it("sends email when button is clicked", async () => {
+ // true = email sent successfully
+ const onRequestNewEmailMock = jest.fn().mockResolvedValue(true);
+
+ const component = render(
+ ,
+ );
+ const sendEmailButton = screen.getByTestId("dialog-send-email-button");
+ sendEmailButton.click();
+
+ expect(onRequestNewEmailMock).toHaveBeenCalled();
+ // wait spinner is displayed
+ expect(component.container.querySelector(".mx_InlineSpinner")).toBeTruthy();
+
+ await flushPromises();
+ // confirmation message is displayed
+ screen.getByTestId("dialog-email-sent-message");
+ });
+
+ it("tells user if sending email failed", async () => {
+ // false = email sending failed
+ const onRequestNewEmailMock = jest.fn().mockResolvedValue(false);
+
+ const component = render(
+ ,
+ );
+ const sendEmailButton = screen.getByTestId("dialog-send-email-button");
+ sendEmailButton.click();
+
+ expect(onRequestNewEmailMock).toHaveBeenCalled();
+ // wait spinner is displayed
+ expect(component.container.querySelector(".mx_InlineSpinner")).toBeTruthy();
+
+ await flushPromises();
+ // confirmation message is displayed
+ screen.getByTestId("dialog-email-failure-message");
+ });
+
+ it("sends only one email when button is clicked twice", async () => {
+ // true = email sent successfully
+ const onRequestNewEmailMock = jest.fn().mockResolvedValue(true);
+
+ render();
+ const sendEmailButton = screen.getByTestId("dialog-send-email-button");
+
+ sendEmailButton.click();
+ await flushPromises();
+ sendEmailButton.click();
+ await flushPromises();
+ expect(onRequestNewEmailMock).toHaveBeenCalledTimes(1); // only 1 email sent
+ screen.getByTestId("dialog-email-wait-message"); // user notified that they must wait
+ });
+ });
+
it("when 'I renewed' is clicked, and the account is renewed, it displays 'cool' and unblocks user (todo)", () => {});
it("when 'I renewed' is clicked, and the account is not renewed, it displays 'not cool' and keeps dialog up (todo)", () => {});
});
From d9d6fdef98d2e0458996c87abecde0735521ab71 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 16:03:56 +0200
Subject: [PATCH 12/15] I renewed tests
---
.../tchap_translations.json | 4 +-
.../views/dialogs/ExpiredAccountDialog.tsx | 6 +--
.../dialogs/ExpiredAccountDialog-test.tsx | 43 ++++++++++++++++++-
3 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/modules/tchap-translations/tchap_translations.json b/modules/tchap-translations/tchap_translations.json
index 93f87936d..98416f9e0 100644
--- a/modules/tchap-translations/tchap_translations.json
+++ b/modules/tchap-translations/tchap_translations.json
@@ -210,7 +210,7 @@
"Read the Privacy Policy": { "en": "Read the Privacy Policy", "fr": "Lire la Politique de Confidentialité" },
"invite|recents_section": { "en": "Recent direct messages", "fr": "Messages directs récents" },
"invite|suggestions_section": { "en": "Recent direct messages", "fr": "Messages directs récents" },
- "Reload the app": { "en": "Reload page", "fr": "Rafraîchir la page" },
+ "Reload page": { "en": "Reload page", "fr": "Rafraîchir la page" },
"Request a renewal email": { "en": "Request a renewal email", "fr": "Demander l’envoi d’un nouvel email" },
"encryption|access_secret_storage_dialog|restoring": {
"en": "Restoring messages from backup",
@@ -247,7 +247,7 @@
"en": "Your email adress is not allowed on Tchap. Make an autorization request here",
"fr": "Votre adresse mail n'est pas autorisée sur Tchap. Demandez l'accès à Tchap pour votre administration via ce formulaire"
},
- "The app will reload now": {
+ "You can refresh the page to continue your conversations": {
"en": "You can refresh the page to continue your conversations",
"fr": "Vous pouvez dorénavant rafraîchir la page pour continuer vos conversations"
},
diff --git a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
index ad174e7f2..2edf30956 100644
--- a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
+++ b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
@@ -122,7 +122,7 @@ export default class ExpiredAccountDialog extends React.Component
+
{_t(
"Your account is still expired, please follow the link in the email you have received to renew it",
)}
@@ -131,8 +131,8 @@ export default class ExpiredAccountDialog extends React.Component{_t("The app will reload now")}
;
- okButtonText = _t("Reload the app");
+ descriptionMessage =
{_t("You can refresh the page to continue your conversations")}
;
+ okButtonText = _t("Reload page");
alertMessage = null;
requestNewEmailButton = null;
break;
diff --git a/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
index ed592d46a..316cf9f31 100644
--- a/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
+++ b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
@@ -3,8 +3,13 @@ import { render, screen } from "@testing-library/react";
import ExpiredAccountDialog from "~tchap-web/src/tchap/components/views/dialogs/ExpiredAccountDialog";
import { flushPromises } from "~tchap-web/yarn-linked-dependencies/matrix-react-sdk/test/test-utils";
+import TchapUtils from "~tchap-web/src/tchap/util/TchapUtils";
describe("ExpiredAccountDialog", () => {
+ afterEach(() => {
+ jest.resetAllMocks();
+ });
+
describe("'Send email' button", () => {
it("sends email when button is clicked", async () => {
// true = email sent successfully
@@ -60,6 +65,40 @@ describe("ExpiredAccountDialog", () => {
});
});
- it("when 'I renewed' is clicked, and the account is renewed, it displays 'cool' and unblocks user (todo)", () => {});
- it("when 'I renewed' is clicked, and the account is not renewed, it displays 'not cool' and keeps dialog up (todo)", () => {});
+ describe("'I renewed my account' button", () => {
+ it("if the account is renewed, it displays success message and ... ? (todo)", async () => {
+ const onFinishedMock = jest.fn();
+ jest.spyOn(TchapUtils, "isAccountExpired").mockResolvedValue(false);
+
+ const component = render(
+ ,
+ );
+ const iRenewedButton = component.getByTestId("dialog-primary-button");
+
+ iRenewedButton.click();
+ await flushPromises();
+
+ // display happy message todo
+ // "votre compte a été renouvelé"
+ // primary label is "Rafraîchir la page"
+ // click primary : onFinished
+ });
+
+ it("if the account is still not renewed, it displays explanation and keeps dialog up", async () => {
+ const onFinishedMock = jest.fn();
+ jest.spyOn(TchapUtils, "isAccountExpired").mockResolvedValue(true);
+
+ const component = render(
+ ,
+ );
+ const iRenewedButton = component.getByTestId("dialog-primary-button");
+
+ iRenewedButton.click();
+ await flushPromises();
+
+ // display unhappy message
+ screen.getByTestId("dialog-account-still-expired-message");
+ expect(onFinishedMock).not.toHaveBeenCalled();
+ });
+ });
});
From 3588e1d25e899d15fbe5aa03288f91c37425b5db Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 16:48:44 +0200
Subject: [PATCH 13/15] One more test
---
.../dialogs/ExpiredAccountDialog-test.tsx | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)
diff --git a/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
index 316cf9f31..3b6d71022 100644
--- a/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
+++ b/test/unit-tests/tchap/components/views/dialogs/ExpiredAccountDialog-test.tsx
@@ -66,22 +66,27 @@ describe("ExpiredAccountDialog", () => {
});
describe("'I renewed my account' button", () => {
- it("if the account is renewed, it displays success message and ... ? (todo)", async () => {
+ it("if the account is renewed, it displays confirmation message, and offers page reload", async () => {
const onFinishedMock = jest.fn();
jest.spyOn(TchapUtils, "isAccountExpired").mockResolvedValue(false);
const component = render(
,
);
- const iRenewedButton = component.getByTestId("dialog-primary-button");
+ const primaryButton = component.getByTestId("dialog-primary-button");
- iRenewedButton.click();
+ // click "I renewed my account"
+ expect(primaryButton).toHaveTextContent("I renewed");
+ primaryButton.click();
await flushPromises();
- // display happy message todo
- // "votre compte a été renouvelé"
- // primary label is "Rafraîchir la page"
- // click primary : onFinished
+ // Account renewed ! Display confirmation
+ component.getByText("your account has been renewed", { exact: false });
+ expect(primaryButton).toHaveTextContent("Reload page"); // new button text
+
+ // click "Reload page"
+ primaryButton.click();
+ expect(onFinishedMock).toHaveBeenCalled(); // onFinish will do the page reload. The actual reload is out of scope here.
});
it("if the account is still not renewed, it displays explanation and keeps dialog up", async () => {
From bee751b6afa0c98a408a15e744d32fb65a63e8f5 Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 17:02:20 +0200
Subject: [PATCH 14/15] Remove old comment
---
test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts b/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
index f1c763608..9e5cf110c 100644
--- a/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
+++ b/test/unit-tests/tchap/lib/ExpiredAccountHandler-test.ts
@@ -25,16 +25,16 @@ describe("ExpiredAccountHandler", () => {
jest.clearAllMocks();
});
+ // TODO : it would be nice to test that our handler is propoerly registered on app load, in src/vector/index.ts
+ // Gonna require a lot of mocking...
it("displays dialog when account is expired", () => {
- // handler instance is created when import ExpiredAccountHandler is run.
+ // handler instance is created when "import ExpiredAccountHandler" is run.
ExpiredAccountHandler.register();
// Simulate start of app, for handler to initialise
defaultDispatcher.dispatch({ action: "will_start_client" }, true);
// Simulate expired error
- // cli.on(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT)
- // this.eventEmitter.emit(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT, err);
mockClient.emit(HttpApiEvent.ORG_MATRIX_EXPIRED_ACCOUNT);
expect(Modal.createDialog).toHaveBeenCalledWith(
From 7b42a72d18f8a9d96895d5ea6afc8c3e388cdf2e Mon Sep 17 00:00:00 2001
From: Estelle Comment
Date: Thu, 11 Apr 2024 17:17:23 +0200
Subject: [PATCH 15/15] Last cleanup
---
src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx | 2 --
src/tchap/util/TchapUtils.ts | 3 +--
2 files changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
index 2edf30956..4418e43b7 100644
--- a/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
+++ b/src/tchap/components/views/dialogs/ExpiredAccountDialog.tsx
@@ -15,7 +15,6 @@ interface IProps {
interface IState {
emailDelaySecs: number; //delay betwenn 2 emails in seconds, by default 30
- isAccountExpired: boolean; //todo: not used yet
newEmailSentTimestamp: number; //timestamp
ProcessState: ProcessState;
}
@@ -38,7 +37,6 @@ export default class ExpiredAccountDialog extends React.Component