From 0e5f0f24cc34a24b6506a8c7b1959b8856d6e32e Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 20 May 2019 15:16:12 +0100 Subject: [PATCH 1/2] Extract MELS-style comma separated list to shared utility This allows other UI components to use the same formatting for a long list. --- .../views/elements/MemberEventListSummary.js | 38 +++---------------- src/i18n/strings/en_EN.json | 6 +-- src/utils/FormattingUtils.js | 31 +++++++++++++++ 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.js b/src/components/views/elements/MemberEventListSummary.js index 3c58f90a2b..dc9c72df6e 100644 --- a/src/components/views/elements/MemberEventListSummary.js +++ b/src/components/views/elements/MemberEventListSummary.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +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. @@ -13,11 +14,13 @@ 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 React from 'react'; import PropTypes from 'prop-types'; import sdk from '../../../index'; -const MemberAvatar = require('../avatars/MemberAvatar.js'); +import MemberAvatar from '../avatars/MemberAvatar'; import { _t } from '../../../languageHandler'; +import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; module.exports = React.createClass({ displayName: 'MemberEventListSummary', @@ -105,7 +108,7 @@ module.exports = React.createClass({ ); }); - const desc = this._renderCommaSeparatedList(descs); + const desc = formatCommaSeparatedList(descs); return _t('%(nameList)s %(transitionList)s', { nameList: nameList, transitionList: desc }); }); @@ -132,7 +135,7 @@ module.exports = React.createClass({ * included before "and [n] others". */ _renderNameList: function(users) { - return this._renderCommaSeparatedList(users, this.props.summaryLength); + return formatCommaSeparatedList(users, this.props.summaryLength); }, /** @@ -283,35 +286,6 @@ module.exports = React.createClass({ return res; }, - /** - * Constructs a written English string representing `items`, with an optional limit on - * the number of items included in the result. If specified and if the length of - *`items` is greater than the limit, the string "and n others" will be appended onto - * the result. - * If `items` is empty, returns the empty string. If there is only one item, return - * it. - * @param {string[]} items the items to construct a string from. - * @param {number?} itemLimit the number by which to limit the list. - * @returns {string} a string constructed by joining `items` with a comma between each - * item, but with the last item appended as " and [lastItem]". - */ - _renderCommaSeparatedList(items, itemLimit) { - const remaining = itemLimit === undefined ? 0 : Math.max( - items.length - itemLimit, 0, - ); - if (items.length === 0) { - return ""; - } else if (items.length === 1) { - return items[0]; - } else if (remaining > 0) { - items = items.slice(0, itemLimit); - return _t("%(items)s and %(count)s others", { items: items.join(', '), count: remaining } ); - } else { - const lastItem = items.pop(); - return _t("%(items)s and %(lastItem)s", { items: items.join(', '), lastItem: lastItem }); - } - }, - _renderAvatars: function(roomMembers) { const avatars = roomMembers.slice(0, this.props.avatarsMaxLength).map((m) => { return ( diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b886e70422..ad5cdd248d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -253,6 +253,9 @@ "This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.", "Please contact your service administrator to continue using the service.": "Please contact your service administrator to continue using the service.", "Unable to connect to Homeserver. Retrying...": "Unable to connect to Homeserver. Retrying...", + "%(items)s and %(count)s others|other": "%(items)s and %(count)s others", + "%(items)s and %(count)s others|one": "%(items)s and one other", + "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", "Your browser does not support the required cryptography extensions": "Your browser does not support the required cryptography extensions", "Not a valid Riot keyfile": "Not a valid Riot keyfile", "Authentication check failed: incorrect password?": "Authentication check failed: incorrect password?", @@ -1046,9 +1049,6 @@ "%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)schanged their avatar", "%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)schanged their avatar %(count)s times", "%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)schanged their avatar", - "%(items)s and %(count)s others|other": "%(items)s and %(count)s others", - "%(items)s and %(count)s others|one": "%(items)s and one other", - "%(items)s and %(lastItem)s": "%(items)s and %(lastItem)s", "collapse": "collapse", "expand": "expand", "Power level": "Power level", diff --git a/src/utils/FormattingUtils.js b/src/utils/FormattingUtils.js index b461d22079..1fd7d00feb 100644 --- a/src/utils/FormattingUtils.js +++ b/src/utils/FormattingUtils.js @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +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. @@ -14,6 +15,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { _t } from '../languageHandler'; + /** * formats numbers to fit into ~3 characters, suitable for badge counts * e.g: 999, 9.9K, 99K, 0.9M, 9.9M, 99M, 0.9B, 9.9B @@ -63,3 +66,31 @@ export function getUserNameColorClass(userId) { const colorNumber = (hashCode(userId) % 8) + 1; return `mx_Username_color${colorNumber}`; } + +/** + * Constructs a written English string representing `items`, with an optional + * limit on the number of items included in the result. If specified and if the + * length of `items` is greater than the limit, the string "and n others" will + * be appended onto the result. If `items` is empty, returns the empty string. + * If there is only one item, return it. + * @param {string[]} items the items to construct a string from. + * @param {number?} itemLimit the number by which to limit the list. + * @returns {string} a string constructed by joining `items` with a comma + * between each item, but with the last item appended as " and [lastItem]". + */ +export function formatCommaSeparatedList(items, itemLimit) { + const remaining = itemLimit === undefined ? 0 : Math.max( + items.length - itemLimit, 0, + ); + if (items.length === 0) { + return ""; + } else if (items.length === 1) { + return items[0]; + } else if (remaining > 0) { + items = items.slice(0, itemLimit); + return _t("%(items)s and %(count)s others", { items: items.join(', '), count: remaining } ); + } else { + const lastItem = items.pop(); + return _t("%(items)s and %(lastItem)s", { items: items.join(', '), lastItem: lastItem }); + } +} From 1bc9badeacd85ac5b07a096114f3cc205c1b7e8d Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 20 May 2019 16:02:10 +0100 Subject: [PATCH 2/2] Limit reaction sender tooltip to 6 people This limits the number of senders shown in the reaction sender tooltip shown when hovering a reaction to 6 people followed by "and N others" for the rest. Fixes https://github.com/vector-im/riot-web/issues/9722 --- src/components/views/messages/ReactionsRowButtonTooltip.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/messages/ReactionsRowButtonTooltip.js b/src/components/views/messages/ReactionsRowButtonTooltip.js index 4f26cea708..e9ec58e8d0 100644 --- a/src/components/views/messages/ReactionsRowButtonTooltip.js +++ b/src/components/views/messages/ReactionsRowButtonTooltip.js @@ -21,6 +21,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg'; import sdk from '../../../index'; import { unicodeToShort } from '../../../HtmlUtils'; import { _t } from '../../../languageHandler'; +import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; export default class ReactionsRowButtonTooltip extends React.PureComponent { static propTypes = { @@ -54,7 +55,7 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent { { reactors: () => { return
- {senders.join(", ")} + {formatCommaSeparatedList(senders, 6)}
; }, reactedWith: (sub) => {