From 511ad141371300dd28a8804657c095cd9f201690 Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Thu, 3 Dec 2020 11:24:44 -0600 Subject: [PATCH] Fix mutation in CallingParticipantList component --- ts/components/CallingParticipantsList.tsx | 3 ++- ts/components/GroupV1MigrationDialog.tsx | 7 ++---- ts/test/util/sortByTitle_test.ts | 29 +++++++++++++++++++++++ ts/util/sortByTitle.ts | 12 ++++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 ts/test/util/sortByTitle_test.ts create mode 100644 ts/util/sortByTitle.ts diff --git a/ts/components/CallingParticipantsList.tsx b/ts/components/CallingParticipantsList.tsx index 207185e363c7..1bb62c687754 100644 --- a/ts/components/CallingParticipantsList.tsx +++ b/ts/components/CallingParticipantsList.tsx @@ -10,6 +10,7 @@ import { ContactName } from './conversation/ContactName'; import { InContactsIcon } from './InContactsIcon'; import { LocalizerType } from '../types/Util'; import { GroupCallPeekedParticipantType } from '../types/Calling'; +import { sortByTitle } from '../util/sortByTitle'; interface ParticipantType extends GroupCallPeekedParticipantType { hasAudio?: boolean; @@ -27,7 +28,7 @@ export const CallingParticipantsList = React.memo( const [root, setRoot] = React.useState(null); const sortedParticipants = React.useMemo>( - () => participants.sort((a, b) => a.title.localeCompare(b.title)), + () => sortByTitle(participants), [participants] ); diff --git a/ts/components/GroupV1MigrationDialog.tsx b/ts/components/GroupV1MigrationDialog.tsx index 88306b89deea..5c57830eb725 100644 --- a/ts/components/GroupV1MigrationDialog.tsx +++ b/ts/components/GroupV1MigrationDialog.tsx @@ -6,6 +6,7 @@ import classNames from 'classnames'; import { LocalizerType } from '../types/Util'; import { ConversationType } from '../state/ducks/conversations'; import { Avatar } from './Avatar'; +import { sortByTitle } from '../util/sortByTitle'; export type ActionSpec = { text: string; @@ -36,10 +37,6 @@ function focusRef(el: HTMLElement | null) { } } -function sort(list: Array): Array { - return [...list].sort((a, b) => a.title.localeCompare(b.title)); -} - export const GroupV1MigrationDialog = React.memo((props: PropsType) => { const { areWeInvited, @@ -172,7 +169,7 @@ function renderMembers(
{i18n(key)}
- {sort(members).map(member => ( + {sortByTitle(members).map(member => (
{ + it("does nothing to arrays that don't need to be sorted", () => { + assert.deepEqual(sortByTitle([]), []); + + assert.deepEqual(sortByTitle([{ title: 'foo' }]), [{ title: 'foo' }]); + }); + + it('sorts the array by title', () => { + // Because the function relies on locale-aware comparisons, we don't have very + // thorough tests here, as it can change based on platform. + assert.deepEqual(sortByTitle([{ title: 'foo' }, { title: 'bar' }]), [ + { title: 'bar' }, + { title: 'foo' }, + ]); + }); + + it("doesn't mutate its argument", () => { + const arr = [{ title: 'foo' }, { title: 'bar' }]; + sortByTitle(arr); + assert.deepEqual(arr, [{ title: 'foo' }, { title: 'bar' }]); + }); +}); diff --git a/ts/util/sortByTitle.ts b/ts/util/sortByTitle.ts new file mode 100644 index 000000000000..1e9d346d65a8 --- /dev/null +++ b/ts/util/sortByTitle.ts @@ -0,0 +1,12 @@ +// Copyright 2020 Signal Messenger, LLC +// SPDX-License-Identifier: AGPL-3.0-only + +interface HasTitle { + title: string; +} + +export function sortByTitle( + arr: ReadonlyArray +): Array { + return [...arr].sort((a, b) => a.title.localeCompare(b.title)); +}