From 8567ce585b0ccc339d569e20fbb41a183b7018aa Mon Sep 17 00:00:00 2001
From: David Baker <dave@matrix.org>
Date: Thu, 16 Aug 2018 13:31:17 +0100
Subject: [PATCH] Factor out duplication of resource limit i18n

---
 src/components/structures/RoomStatusBar.js    | 39 +++++------
 src/components/structures/login/Login.js      | 38 +++++-----
 .../structures/login/Registration.js          | 36 ++++++----
 .../views/globals/ServerLimitBar.js           | 69 +++++++++----------
 src/i18n/strings/en_EN.json                   |  1 -
 src/utils/ErrorUtils.js                       | 50 ++++++++++++++
 6 files changed, 143 insertions(+), 90 deletions(-)
 create mode 100644 src/utils/ErrorUtils.js

diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js
index 260aec81ea..fec59aadd5 100644
--- a/src/components/structures/RoomStatusBar.js
+++ b/src/components/structures/RoomStatusBar.js
@@ -18,7 +18,7 @@ limitations under the License.
 import React from 'react';
 import PropTypes from 'prop-types';
 import Matrix from 'matrix-js-sdk';
-import { _t } from '../../languageHandler';
+import { _t, _td } from '../../languageHandler';
 import sdk from '../../index';
 import WhoIsTyping from '../../WhoIsTyping';
 import MatrixClientPeg from '../../MatrixClientPeg';
@@ -26,6 +26,7 @@ import MemberAvatar from '../views/avatars/MemberAvatar';
 import Resend from '../../Resend';
 import * as cryptodevices from '../../cryptodevices';
 import dis from '../../dispatcher';
+import { messageForResourceLimitError } from '../../utils/ErrorUtils';
 
 const STATUS_BAR_HIDDEN = 0;
 const STATUS_BAR_EXPANDED = 1;
@@ -326,20 +327,13 @@ module.exports = React.createClass({
             );
         } else {
             let consentError = null;
-            let mauError = null;
-            const translateMauError = sub => {
-                if (mauError.data.admin_contact) {
-                    return <a href={mauError.data.admin_contact} target="_blank" rel="noopener">{sub}</a>;
-                } else {
-                    return sub;
-                }
-            };
+            let resourceLimitError = null;
             for (const m of unsentMessages) {
                 if (m.error && m.error.errcode === 'M_CONSENT_NOT_GIVEN') {
                     consentError = m.error;
                     break;
                 } else if (m.error && m.error.errcode === 'M_RESOURCE_LIMIT_EXCEEDED') {
-                    mauError = m.error;
+                    resourceLimitError = m.error;
                     break;
                 }
             }
@@ -355,18 +349,19 @@ module.exports = React.createClass({
                             </a>,
                     },
                 );
-            } else if (mauError && mauError.data.limit_type === 'monthly_active_user') {
-                title = _t(
-                    "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " +
-                    "Please <a>contact your service administrator</a> to continue using the service.",
-                    {}, { 'a' : translateMauError },
-                );
-            } else if (mauError) {
-                title = _t(
-                    "Your message wasn't sent because this homeserver has exceeded a resource limit. " +
-                    "Please <a>contact your service administrator</a> to continue using the service.",
-                    {}, { 'a' : translateMauError },
-                );
+            } else if (resourceLimitError) {
+                title = messageForResourceLimitError(
+                    resourceLimitError.data.limit_type,
+                    resourceLimitError.data.admin_contact, {
+                    'monthly_active_user': _td(
+                        "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " +
+                        "Please <a>contact your service administrator</a> to continue using the service.",
+                    ),
+                    '': _td(
+                        "Your message wasn't sent because this homeserver has exceeded a resource limit. " +
+                        "Please <a>contact your service administrator</a> to continue using the service.",
+                    ),
+                });
             } else if (
                 unsentMessages.length === 1 &&
                 unsentMessages[0].error &&
diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js
index 77db6ea444..45f523f141 100644
--- a/src/components/structures/login/Login.js
+++ b/src/components/structures/login/Login.js
@@ -20,11 +20,12 @@ limitations under the License.
 
 import React from 'react';
 import PropTypes from 'prop-types';
-import { _t } from '../../../languageHandler';
+import { _t, _td } from '../../../languageHandler';
 import sdk from '../../../index';
 import Login from '../../../Login';
 import SdkConfig from '../../../SdkConfig';
 import SettingsStore from "../../../settings/SettingsStore";
+import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
 
 // For validating phone numbers without country codes
 const PHONE_NUMBER_REGEX = /^[0-9()\-\s]*$/;
@@ -122,24 +123,27 @@ module.exports = React.createClass({
             if (error.httpStatus === 400 && usingEmail) {
                 errorText = _t('This Home Server does not support login using email address.');
             } else if (error.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') {
+                const errorTop = messageForResourceLimitError(
+                    error.data.limit_type,
+                    error.data.admin_contact, {
+                    'monthly_active_user': _td(
+                        "This homeserver has hit its Monthly Active User limit.",
+                    ),
+                    '': _td(
+                        "This homeserver has exceeded one of its resource limits.",
+                    ),
+                });
+                const errorDetail = messageForResourceLimitError(
+                    error.data.limit_type,
+                    error.data.admin_contact, {
+                    '': _td(
+                        "Please <a>contact your service administrator</a> to continue using this service.",
+                    ),
+                });
                 errorText = (
                     <div>
-                        <div>{error.data.error ? error.data.error : _t("This server has exceeded its available resources")}</div>
-                        <div className="mx_Login_smallError">
-                            {_t(
-                                "Please <a>contact your service administrator</a> to continue using this service.",
-                                {},
-                                {
-                                    a: (sub) => {
-                                        if (error.data.admin_contact) {
-                                            return <a rel="noopener" target="_blank" href={error.data.admin_contact}>{sub}</a>;
-                                        } else {
-                                            return sub;
-                                        }
-                                    },
-                                },
-                            )}
-                        </div>
+                        <div>{errorTop}</div>
+                        <div className="mx_Login_smallError">{errorDetail}</div>
                     </div>
                 );
             } else if (error.httpStatus === 401 || error.httpStatus === 403) {
diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js
index 3e2b3c2bdc..1131218311 100644
--- a/src/components/structures/login/Registration.js
+++ b/src/components/structures/login/Registration.js
@@ -26,9 +26,10 @@ import sdk from '../../../index';
 import MatrixClientPeg from '../../../MatrixClientPeg';
 import RegistrationForm from '../../views/login/RegistrationForm';
 import RtsClient from '../../../RtsClient';
-import { _t } from '../../../languageHandler';
+import { _t, _td } from '../../../languageHandler';
 import SdkConfig from '../../../SdkConfig';
 import SettingsStore from "../../../settings/SettingsStore";
+import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
 
 const MIN_PASSWORD_LENGTH = 6;
 
@@ -165,21 +166,26 @@ module.exports = React.createClass({
             let msg = response.message || response.toString();
             // can we give a better error message?
             if (response.errcode == 'M_RESOURCE_LIMIT_EXCEEDED') {
-                msg = <div>
-                    <p>{response.data.error ? response.data.error : _t("This server has exceeded its available resources")}</p>
-                    <p>{_t(
+                const errorTop = messageForResourceLimitError(
+                    response.data.limit_type,
+                    response.data.admin_contact, {
+                    'monthly_active_user': _td(
+                        "This homeserver has hit its Monthly Active User limit.",
+                    ),
+                    '': _td(
+                        "This homeserver has exceeded one of its resource limits.",
+                    ),
+                });
+                const errorDetail = messageForResourceLimitError(
+                    response.data.limit_type,
+                    response.data.admin_contact, {
+                    '': _td(
                         "Please <a>contact your service administrator</a> to continue using this service.",
-                        {},
-                        {
-                            a: (sub) => {
-                                if (response.data.admin_contact) {
-                                    return <a rel="noopener" target="_blank" href={response.data.admin_contact}>{sub}</a>;
-                                } else {
-                                    return sub;
-                                }
-                            },
-                        },
-                    )}</p>
+                    ),
+                });
+                msg = <div>
+                    <p>{errorTop}</p>
+                    <p>{errorDetail}</p>
                 </div>;
             } else if (response.required_stages && response.required_stages.indexOf('m.login.msisdn') > -1) {
                 let msisdnAvailable = false;
diff --git a/src/components/views/globals/ServerLimitBar.js b/src/components/views/globals/ServerLimitBar.js
index 375d52bbe4..0b924fd2e2 100644
--- a/src/components/views/globals/ServerLimitBar.js
+++ b/src/components/views/globals/ServerLimitBar.js
@@ -17,7 +17,8 @@ limitations under the License.
 import React from 'react';
 import PropTypes from 'prop-types';
 import classNames from 'classnames';
-import { _t } from '../../../languageHandler';
+import { _td } from '../../../languageHandler';
+import { messageForResourceLimitError } from '../../../utils/ErrorUtils';
 
 export default React.createClass({
     propTypes: {
@@ -39,52 +40,50 @@ export default React.createClass({
             'mx_MatrixToolbar': true,
         };
 
-        const translateLink = (sub) => {
-            if (this.props.adminContact) {
-                return <a rel="noopener" target="_blank" href={this.props.adminContact}>{sub}</a>;
-            } else {
-                return sub;
-            }
-        };
-
         let adminContact;
         let limitError;
         if (this.props.kind === 'hard') {
             toolbarClasses['mx_MatrixToolbar_error'] = true;
-            adminContact = _t(
-                "Please <a>contact your service administrator</a> to continue using the service.",
-                {},
+
+            adminContact = messageForResourceLimitError(
+                this.props.limitType,
+                this.props.adminContact,
                 {
-                    'a': translateLink,
+                    '': _td("Please <a>contact your service administrator</a> to continue using the service."),
+                },
+            );
+            limitError = messageForResourceLimitError(
+                this.props.limitType,
+                this.props.adminContact,
+                {
+                    'monthly_active_user': _td("This homeserver has hit its Monthly Active User limit."),
+                    '': _td("This homeserver has exceeded one of its resource limits."),
                 },
             );
-            if (this.props.limitType === 'monthly_active_user') {
-                limitError = _t("This homeserver has hit its Monthly Active User limit.");
-            } else {
-                limitError = _t("This homeserver has exceeded one of its resource limits.");
-            }
         } else {
             toolbarClasses['mx_MatrixToolbar_info'] = true;
-            adminContact = _t(
-                "Please <a>contact your service administrator</a> to get this limit increased.",
-                {},
+            adminContact = messageForResourceLimitError(
+                this.props.limitType,
+                this.props.adminContact,
                 {
-                    'a': translateLink,
+                    '': _td("Please <a>contact your service administrator</a> to get this limit increased."),
                 },
             );
-            if (this.props.limitType === 'monthly_active_user') {
-                limitError = _t(
-                    "This homeserver has hit its Monthly Active User limit so " +
-                    "<b>some users will not be able to log in</b>.", {},
-                    {'b': sub => <b>{sub}</b>},
-                );
-            } else {
-                limitError = _t(
-                    "This homeserver has exceeded one of its resource limits so " +
-                    "<b>some users will not be able to log in</b>.", {},
-                    {'b': sub => <b>{sub}</b>},
-                );
-            }
+            limitError = messageForResourceLimitError(
+                this.props.limitType,
+                this.props.adminContact,
+                {
+                    'monthly_active_user': _td(
+                        "This homeserver has hit its Monthly Active User limit so " +
+                        "<b>some users will not be able to log in</b>.",
+                    ),
+                    '': _td(
+                        "This homeserver has exceeded one of its resource limits so " +
+                        "<b>some users will not be able to log in</b>.",
+                    ),
+                },
+                {'b': sub => <b>{sub}</b>},
+            );
         }
         return (
             <div className={classNames(toolbarClasses)}>
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 68a4ce1508..1afa35d8fd 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1171,7 +1171,6 @@
     "Send Reset Email": "Send Reset Email",
     "Create an account": "Create an account",
     "This Home Server does not support login using email address.": "This Home Server does not support login using email address.",
-    "This server has exceeded its available resources": "This server has exceeded its available resources",
     "Please <a>contact your service administrator</a> to continue using this service.": "Please <a>contact your service administrator</a> to continue using this service.",
     "Incorrect username and/or password.": "Incorrect username and/or password.",
     "Please note you are logging into the %(hs)s server, not matrix.org.": "Please note you are logging into the %(hs)s server, not matrix.org.",
diff --git a/src/utils/ErrorUtils.js b/src/utils/ErrorUtils.js
new file mode 100644
index 0000000000..97a29a3572
--- /dev/null
+++ b/src/utils/ErrorUtils.js
@@ -0,0 +1,50 @@
+/*
+Copyright 2018 New Vector Ltd
+
+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 { _t } from '../languageHandler';
+
+/**
+ * Produce a translated error message for a
+ * M_RESOURCE_LIMIT_EXCEEDED error
+ *
+ * @param {string} limitType The limit_type from the error
+ * @param {string} adminContact The admin_contact from the error
+ * @param {Object} strings Translateable string for different
+ *     limit_type. Must include at least the empty string key
+ *     which is the default. Strings may include an 'a' tag
+ *     for the admin contact link.
+ * @param {Object} extraTranslations Extra translation substitution functions
+ *     for any tags in the strings apart from 'a'
+ * @returns {*} Translated string or react component
+ */
+export function messageForResourceLimitError(limitType, adminContact, strings, extraTranslations) {
+    let errString = strings[limitType];
+    if (errString === undefined) errString = strings[''];
+
+    const linkSub = sub => {
+        if (adminContact) {
+            return <a href={adminContact} target="_blank" rel="noopener">{sub}</a>;
+        } else {
+            return sub;
+        }
+    };
+
+    if (errString.includes('<a>')) {
+        return _t(errString, {}, Object.assign({ 'a': linkSub }, extraTranslations));
+    } else {
+        return _t(errString, {}, extraTranslations);
+    }
+}