From 7a329b7a018d9343c7514c3c5ef83edce97c345e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 11 Jul 2021 10:31:24 +0200 Subject: [PATCH 01/10] Add ReactUtils MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/utils/ReactUtils.tsx | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/utils/ReactUtils.tsx diff --git a/src/utils/ReactUtils.tsx b/src/utils/ReactUtils.tsx new file mode 100644 index 0000000000..ce92dd8a51 --- /dev/null +++ b/src/utils/ReactUtils.tsx @@ -0,0 +1,33 @@ +/* +Copyright 2021 Šimon Brandner + +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 React from "react"; + +/** + * Joins an array into one value with a joiner. E.g. join(["hello", "world"], " ") -> hello world + * @param array the array of element to join + * @param joiner the string/JSX.Element to join with + * @returns the joined array + */ +export function join(array: Array, joiner?: string | JSX.Element): JSX.Element { + const newArray = []; + array.forEach((element, index) => { + newArray.push(element, (index === array.length - 1) ? null : joiner); + }); + return ( + { newArray } + ); +} From 3e95cd1854017de2cf7cd3e6ecda9c4c09992bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 11 Jul 2021 10:34:15 +0200 Subject: [PATCH 02/10] Handle JSX in MELS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/EventListSummary.tsx | 2 +- .../views/elements/MemberEventListSummary.tsx | 14 +++++++++++--- src/utils/FormattingUtils.ts | 7 ++++--- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx index 681817ca86..4f6df2aa0e 100644 --- a/src/components/views/elements/EventListSummary.tsx +++ b/src/components/views/elements/EventListSummary.tsx @@ -33,7 +33,7 @@ interface IProps { // The list of room members for which to show avatars next to the summary summaryMembers?: RoomMember[]; // The text to show as the summary of this event list - summaryText?: string; + summaryText?: string | JSX.Element; // An array of EventTiles to render when expanded children: ReactNode[]; // Called when the event list expansion is toggled diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index d52462f629..cef6195067 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -25,6 +25,7 @@ import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; import { isValid3pidInvite } from "../../../RoomInvite"; import EventListSummary from "./EventListSummary"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { join } from '../../../utils/ReactUtils'; interface IProps extends Omit, "summaryText" | "summaryMembers"> { // The maximum number of names to show in either each summary e.g. 2 would result "A, B and 234 others left" @@ -89,7 +90,10 @@ export default class MemberEventListSummary extends React.Component { * `Object.keys(eventAggregates)`. * @returns {string} the textual summary of the aggregated events that occurred. */ - private generateSummary(eventAggregates: Record, orderedTransitionSequences: string[]) { + private generateSummary( + eventAggregates: Record, + orderedTransitionSequences: string[], + ): string | JSX.Element { const summaries = orderedTransitionSequences.map((transitions) => { const userNames = eventAggregates[transitions]; const nameList = this.renderNameList(userNames); @@ -118,7 +122,7 @@ export default class MemberEventListSummary extends React.Component { return null; } - return summaries.join(", "); + return join(summaries, ", "); } /** @@ -212,7 +216,11 @@ export default class MemberEventListSummary extends React.Component { * @param {number} repeats the number of times the transition was repeated in a row. * @returns {string} the written Human Readable equivalent of the transition. */ - private static getDescriptionForTransition(t: TransitionType, userCount: number, repeats: number) { + private static getDescriptionForTransition( + t: TransitionType, + userCount: number, + repeats: number, + ): string | JSX.Element { // The empty interpolations 'severalUsers' and 'oneUser' // are there only to show translators to non-English languages // that the verb is conjugated to plural or singular Subject. diff --git a/src/utils/FormattingUtils.ts b/src/utils/FormattingUtils.ts index 1fe3669f26..53a4adb238 100644 --- a/src/utils/FormattingUtils.ts +++ b/src/utils/FormattingUtils.ts @@ -16,6 +16,7 @@ limitations under the License. */ import { _t } from '../languageHandler'; +import { join } from './ReactUtils'; /** * formats numbers to fit into ~3 characters, suitable for badge counts @@ -103,7 +104,7 @@ export function getUserNameColorClass(userId: string): string { * @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: string[], itemLimit?: number): string { +export function formatCommaSeparatedList(items: Array, itemLimit?: number): string | JSX.Element { const remaining = itemLimit === undefined ? 0 : Math.max( items.length - itemLimit, 0, ); @@ -113,9 +114,9 @@ export function formatCommaSeparatedList(items: string[], itemLimit?: number): s 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 } ); + return _t("%(items)s and %(count)s others", { items: join(items, ', '), count: remaining } ); } else { const lastItem = items.pop(); - return _t("%(items)s and %(lastItem)s", { items: items.join(', '), lastItem: lastItem }); + return _t("%(items)s and %(lastItem)s", { items: join(items, ', '), lastItem: lastItem }); } } From f80f4620dfec779c6b47c3bc059e6a4d86f124d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 11 Jul 2021 10:35:20 +0200 Subject: [PATCH 03/10] Add pinned messages to MELS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/MessagePanel.tsx | 7 +++- .../views/elements/MemberEventListSummary.tsx | 39 +++++++++++++++---- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index a0a1ac9b10..16b1c0064b 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -50,7 +50,12 @@ import EditorStateTransfer from "../../utils/EditorStateTransfer"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = [EventType.Sticker, EventType.RoomMessage]; -const membershipTypes = [EventType.RoomMember, EventType.RoomThirdPartyInvite, EventType.RoomServerAcl]; +const membershipTypes = [ + EventType.RoomMember, + EventType.RoomThirdPartyInvite, + EventType.RoomServerAcl, + EventType.RoomPinnedEvents, +]; // check if there is a previous event and it has the same sender as this event // and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index cef6195067..80efb2bea8 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -25,7 +25,22 @@ import { formatCommaSeparatedList } from '../../../utils/FormattingUtils'; import { isValid3pidInvite } from "../../../RoomInvite"; import EventListSummary from "./EventListSummary"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import defaultDispatcher from '../../../dispatcher/dispatcher'; +import { RightPanelPhases } from '../../../stores/RightPanelStorePhases'; +import { Action } from '../../../dispatcher/actions'; +import { SetRightPanelPhasePayload } from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; import { join } from '../../../utils/ReactUtils'; +import { EventType } from '../../../../../matrix-js-sdk/src/@types/event'; + +const onPinnedMessagesClick = (): void => { + defaultDispatcher.dispatch({ + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.PinnedMessages, + allowClose: false, + }); +}; + +const SENDER_AS_DISPLAY_NAME_EVENTS = [EventType.RoomServerAcl, EventType.RoomPinnedEvents]; interface IProps extends Omit, "summaryText" | "summaryMembers"> { // The maximum number of names to show in either each summary e.g. 2 would result "A, B and 234 others left" @@ -58,6 +73,7 @@ enum TransitionType { ChangedAvatar = "changed_avatar", NoChange = "no_change", ServerAcl = "server_acl", + PinnedMessages = "pinned_messages" } const SEP = ","; @@ -303,6 +319,15 @@ export default class MemberEventListSummary extends React.Component { { severalUsers: "", count: repeats }) : _t("%(oneUser)schanged the server ACLs %(count)s times", { oneUser: "", count: repeats }); break; + case "pinned_messages": + res = (userCount > 1) + ? _t("%(severalUsers)schanged the pinned messages for the room %(count)s times.", + { severalUsers: "", count: repeats }, + { "a": (sub) => { sub } }) + : _t("%(oneUser)schanged the pinned messages for the room %(count)s times.", + { oneUser: "", count: repeats }, + { "a": (sub) => { sub } }); + break; } return res; @@ -321,16 +346,16 @@ export default class MemberEventListSummary extends React.Component { * if a transition is not recognised. */ private static getTransition(e: IUserEvents): TransitionType { - if (e.mxEvent.getType() === 'm.room.third_party_invite') { + if (e.mxEvent.getType() === EventType.RoomThirdPartyInvite) { // Handle 3pid invites the same as invites so they get bundled together if (!isValid3pidInvite(e.mxEvent)) { return TransitionType.InviteWithdrawal; } return TransitionType.Invited; - } - - if (e.mxEvent.getType() === 'm.room.server_acl') { + } else if (e.mxEvent.getType() === EventType.RoomServerAcl) { return TransitionType.ServerAcl; + } else if (e.mxEvent.getType() === EventType.RoomPinnedEvents) { + return TransitionType.PinnedMessages; } switch (e.mxEvent.getContent().membership) { @@ -425,16 +450,16 @@ export default class MemberEventListSummary extends React.Component { userEvents[userId] = []; } - if (e.getType() === 'm.room.server_acl') { + if (SENDER_AS_DISPLAY_NAME_EVENTS.includes(e.getType() as EventType)) { latestUserAvatarMember.set(userId, e.sender); } else if (e.target) { latestUserAvatarMember.set(userId, e.target); } let displayName = userId; - if (e.getType() === 'm.room.third_party_invite') { + if (e.getType() === EventType.RoomThirdPartyInvite) { displayName = e.getContent().display_name; - } else if (e.getType() === 'm.room.server_acl') { + } else if (SENDER_AS_DISPLAY_NAME_EVENTS.includes(e.getType() as EventType)) { displayName = e.sender.name; } else if (e.target) { displayName = e.target.name; From 1b993ef1bde87f797ce4af6dbb9b5708ca56c430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 11 Jul 2021 10:36:56 +0200 Subject: [PATCH 04/10] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7795bb2610..9c22efdb3e 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2058,6 +2058,8 @@ "%(severalUsers)schanged the server ACLs %(count)s times|one": "%(severalUsers)schanged the server ACLs", "%(oneUser)schanged the server ACLs %(count)s times|other": "%(oneUser)schanged the server ACLs %(count)s times", "%(oneUser)schanged the server ACLs %(count)s times|one": "%(oneUser)schanged the server ACLs", + "%(severalUsers)schanged the pinned messages for the room %(count)s times.|other": "%(severalUsers)schanged the pinned messages for the room %(count)s times.", + "%(oneUser)schanged the pinned messages for the room %(count)s times.|other": "%(oneUser)schanged the pinned messages for the room %(count)s times.", "Power level": "Power level", "Custom level": "Custom level", "QR Code": "QR Code", From db8ebd6df009f268ced03b2f1bacedec5b3c300f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 11 Jul 2021 10:45:16 +0200 Subject: [PATCH 05/10] Fix import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/MemberEventListSummary.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index 80efb2bea8..e3dbd0a906 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -30,7 +30,7 @@ import { RightPanelPhases } from '../../../stores/RightPanelStorePhases'; import { Action } from '../../../dispatcher/actions'; import { SetRightPanelPhasePayload } from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; import { join } from '../../../utils/ReactUtils'; -import { EventType } from '../../../../../matrix-js-sdk/src/@types/event'; +import { EventType } from 'matrix-js-sdk/src/@types/event'; const onPinnedMessagesClick = (): void => { defaultDispatcher.dispatch({ From 29ef5905d60b14f3751698afb96cffe3c663b6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 11 Jul 2021 10:45:30 +0200 Subject: [PATCH 06/10] Export type into a var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/elements/MemberEventListSummary.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index e3dbd0a906..4ae64b65a5 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -346,15 +346,17 @@ export default class MemberEventListSummary extends React.Component { * if a transition is not recognised. */ private static getTransition(e: IUserEvents): TransitionType { - if (e.mxEvent.getType() === EventType.RoomThirdPartyInvite) { + const type = e.mxEvent.getType(); + + if (type === EventType.RoomThirdPartyInvite) { // Handle 3pid invites the same as invites so they get bundled together if (!isValid3pidInvite(e.mxEvent)) { return TransitionType.InviteWithdrawal; } return TransitionType.Invited; - } else if (e.mxEvent.getType() === EventType.RoomServerAcl) { + } else if (type === EventType.RoomServerAcl) { return TransitionType.ServerAcl; - } else if (e.mxEvent.getType() === EventType.RoomPinnedEvents) { + } else if (type === EventType.RoomPinnedEvents) { return TransitionType.PinnedMessages; } @@ -444,22 +446,23 @@ export default class MemberEventListSummary extends React.Component { // Object mapping user IDs to an array of IUserEvents const userEvents: Record = {}; eventsToRender.forEach((e, index) => { - const userId = e.getType() === 'm.room.server_acl' ? e.getSender() : e.getStateKey(); + const type = e.getType(); + const userId = type === EventType.RoomServerAcl ? e.getSender() : e.getStateKey(); // Initialise a user's events if (!userEvents[userId]) { userEvents[userId] = []; } - if (SENDER_AS_DISPLAY_NAME_EVENTS.includes(e.getType() as EventType)) { + if (SENDER_AS_DISPLAY_NAME_EVENTS.includes(type as EventType)) { latestUserAvatarMember.set(userId, e.sender); } else if (e.target) { latestUserAvatarMember.set(userId, e.target); } let displayName = userId; - if (e.getType() === EventType.RoomThirdPartyInvite) { + if (type === EventType.RoomThirdPartyInvite) { displayName = e.getContent().display_name; - } else if (SENDER_AS_DISPLAY_NAME_EVENTS.includes(e.getType() as EventType)) { + } else if (SENDER_AS_DISPLAY_NAME_EVENTS.includes(type as EventType)) { displayName = e.sender.name; } else if (e.target) { displayName = e.target.name; From 19f14e4b2e6495cbbe9b7c70f9753b1005ed2e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 11 Jul 2021 10:58:04 +0200 Subject: [PATCH 07/10] Fix tests? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/utils/ReactUtils.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/ReactUtils.tsx b/src/utils/ReactUtils.tsx index ce92dd8a51..25669d2d9b 100644 --- a/src/utils/ReactUtils.tsx +++ b/src/utils/ReactUtils.tsx @@ -28,6 +28,6 @@ export function join(array: Array, joiner?: string | JSX.E newArray.push(element, (index === array.length - 1) ? null : joiner); }); return ( - { newArray } + { newArray } ); } From e7c5711bb76c93a9cd7e8215a847a247aa54d764 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 22 Jul 2021 07:45:48 +0200 Subject: [PATCH 08/10] membershipTypes -> groupedEvents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/MessagePanel.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 16b1c0064b..1e113b0b7b 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -50,7 +50,7 @@ import EditorStateTransfer from "../../utils/EditorStateTransfer"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = [EventType.Sticker, EventType.RoomMessage]; -const membershipTypes = [ +const groupedEvents = [ EventType.RoomMember, EventType.RoomThirdPartyInvite, EventType.RoomServerAcl, @@ -1185,7 +1185,7 @@ class RedactionGrouper extends BaseGrouper { // Wrap consecutive member events in a ListSummary, ignore if redacted class MemberGrouper extends BaseGrouper { static canStartGroup = function(panel: MessagePanel, ev: MatrixEvent): boolean { - return panel.shouldShowEvent(ev) && membershipTypes.includes(ev.getType() as EventType); + return panel.shouldShowEvent(ev) && groupedEvents.includes(ev.getType() as EventType); }; constructor( @@ -1202,7 +1202,7 @@ class MemberGrouper extends BaseGrouper { if (this.panel.wantsDateSeparator(this.events[0], ev.getDate())) { return false; } - return membershipTypes.includes(ev.getType() as EventType); + return groupedEvents.includes(ev.getType() as EventType); } public add(ev: MatrixEvent): void { From 51c112fd821995c4435488a5bbf2974b39ea706d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 22 Jul 2021 07:46:41 +0200 Subject: [PATCH 09/10] PinnedMessages -> ChangedPins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/MemberEventListSummary.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index 4ae64b65a5..3b1557b9ad 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -73,7 +73,7 @@ enum TransitionType { ChangedAvatar = "changed_avatar", NoChange = "no_change", ServerAcl = "server_acl", - PinnedMessages = "pinned_messages" + ChangedPins = "pinned_messages" } const SEP = ","; @@ -357,7 +357,7 @@ export default class MemberEventListSummary extends React.Component { } else if (type === EventType.RoomServerAcl) { return TransitionType.ServerAcl; } else if (type === EventType.RoomPinnedEvents) { - return TransitionType.PinnedMessages; + return TransitionType.ChangedPins; } switch (e.mxEvent.getContent().membership) { From 9f227893b1a093de8b1b41dea724dc3be3058f63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 22 Jul 2021 07:47:04 +0200 Subject: [PATCH 10/10] join -> jsxJoin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/MemberEventListSummary.tsx | 4 ++-- src/utils/FormattingUtils.ts | 6 +++--- src/utils/ReactUtils.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/elements/MemberEventListSummary.tsx b/src/components/views/elements/MemberEventListSummary.tsx index 3b1557b9ad..46a27415ca 100644 --- a/src/components/views/elements/MemberEventListSummary.tsx +++ b/src/components/views/elements/MemberEventListSummary.tsx @@ -29,7 +29,7 @@ import defaultDispatcher from '../../../dispatcher/dispatcher'; import { RightPanelPhases } from '../../../stores/RightPanelStorePhases'; import { Action } from '../../../dispatcher/actions'; import { SetRightPanelPhasePayload } from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; -import { join } from '../../../utils/ReactUtils'; +import { jsxJoin } from '../../../utils/ReactUtils'; import { EventType } from 'matrix-js-sdk/src/@types/event'; const onPinnedMessagesClick = (): void => { @@ -138,7 +138,7 @@ export default class MemberEventListSummary extends React.Component { return null; } - return join(summaries, ", "); + return jsxJoin(summaries, ", "); } /** diff --git a/src/utils/FormattingUtils.ts b/src/utils/FormattingUtils.ts index 53a4adb238..b527ee7ea2 100644 --- a/src/utils/FormattingUtils.ts +++ b/src/utils/FormattingUtils.ts @@ -16,7 +16,7 @@ limitations under the License. */ import { _t } from '../languageHandler'; -import { join } from './ReactUtils'; +import { jsxJoin } from './ReactUtils'; /** * formats numbers to fit into ~3 characters, suitable for badge counts @@ -114,9 +114,9 @@ export function formatCommaSeparatedList(items: Array, ite return items[0]; } else if (remaining > 0) { items = items.slice(0, itemLimit); - return _t("%(items)s and %(count)s others", { items: join(items, ', '), count: remaining } ); + return _t("%(items)s and %(count)s others", { items: jsxJoin(items, ', '), count: remaining } ); } else { const lastItem = items.pop(); - return _t("%(items)s and %(lastItem)s", { items: join(items, ', '), lastItem: lastItem }); + return _t("%(items)s and %(lastItem)s", { items: jsxJoin(items, ', '), lastItem: lastItem }); } } diff --git a/src/utils/ReactUtils.tsx b/src/utils/ReactUtils.tsx index 25669d2d9b..4cd2d750f3 100644 --- a/src/utils/ReactUtils.tsx +++ b/src/utils/ReactUtils.tsx @@ -22,7 +22,7 @@ import React from "react"; * @param joiner the string/JSX.Element to join with * @returns the joined array */ -export function join(array: Array, joiner?: string | JSX.Element): JSX.Element { +export function jsxJoin(array: Array, joiner?: string | JSX.Element): JSX.Element { const newArray = []; array.forEach((element, index) => { newArray.push(element, (index === array.length - 1) ? null : joiner);