diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 63192019f40a..48a196fb25d3 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -1881,6 +1881,10 @@ "messageformat": "{first}, {second}, and {others, plural, one {#} other {#}} others are in this call", "description": "Shown in the calling lobby to describe who is in the call" }, + "icu:calling__pre-call-info--only-unknown-contacts-in-call": { + "messageformat": "{count, plural, one {# person is in this call} other {# people are in this call}}", + "description": "Shown in the calling lobby to describe people in the call when all are unknown contacts" + }, "icu:calling__pre-call-info--will-ring-1": { "messageformat": "Signal will ring {person}", "description": "Shown in the calling lobby to describe who will be rung" diff --git a/ts/components/CallingPreCallInfo.stories.tsx b/ts/components/CallingPreCallInfo.stories.tsx index c20e0d8940f1..a445d1eb0bdf 100644 --- a/ts/components/CallingPreCallInfo.stories.tsx +++ b/ts/components/CallingPreCallInfo.stories.tsx @@ -9,6 +9,11 @@ import enMessages from '../../_locales/en/messages.json'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import type { PropsType } from './CallingPreCallInfo'; import { CallingPreCallInfo, RingMode } from './CallingPreCallInfo'; +import type { ConversationType } from '../state/ducks/conversations'; +import { getPlaceholderContact } from '../state/selectors/conversations'; +import { generateAci } from '../types/ServiceId'; +import { FAKE_CALL_LINK } from '../test-both/helpers/fakeCallLink'; +import { callLinkToConversation } from '../util/callLinks'; const i18n = setupI18n('en', enMessages); const getDefaultGroupConversation = () => @@ -21,6 +26,11 @@ const getDefaultGroupConversation = () => }); const otherMembers = times(6, () => getDefaultConversation()); +const getUnknownContact = (): ConversationType => ({ + ...getPlaceholderContact(), + serviceId: generateAci(), +}); + export default { title: 'Components/CallingPreCallInfo', } satisfies Meta; @@ -245,3 +255,63 @@ export function GroupConversationCallIsFull(): JSX.Element { /> ); } + +export function CallLinkUnknownContact(): JSX.Element { + return ( + + ); +} + +export function CallLink3UnknownContacts(): JSX.Element { + return ( + + ); +} + +export function CallLink1Known1UnknownContact(): JSX.Element { + return ( + + ); +} + +export function CallLink1Known2UnknownContacts(): JSX.Element { + return ( + + ); +} diff --git a/ts/components/CallingPreCallInfo.tsx b/ts/components/CallingPreCallInfo.tsx index 45eba7e2f142..c843b9b5316f 100644 --- a/ts/components/CallingPreCallInfo.tsx +++ b/ts/components/CallingPreCallInfo.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-only import React from 'react'; +import { partition } from 'lodash'; import type { ConversationType } from '../state/ducks/conversations'; import type { CallingConversationType } from '../types/Calling'; import type { LocalizerType } from '../types/Util'; @@ -16,6 +17,16 @@ export enum RingMode { IsRinging, } +type PeekedParticipantType = Pick< + ConversationType, + | 'firstName' + | 'systemGivenName' + | 'systemNickname' + | 'title' + | 'serviceId' + | 'titleNoDefault' +>; + export type PropsType = { conversation: Pick< CallingConversationType, @@ -44,12 +55,7 @@ export type PropsType = { > >; isCallFull?: boolean; - peekedParticipants?: Array< - Pick< - ConversationType, - 'firstName' | 'systemGivenName' | 'systemNickname' | 'title' | 'serviceId' - > - >; + peekedParticipants?: Array; }; export function CallingPreCallInfo({ @@ -61,52 +67,70 @@ export function CallingPreCallInfo({ peekedParticipants = [], ringMode, }: PropsType): JSX.Element { + const [visibleParticipants, unknownParticipants] = React.useMemo< + [Array, Array] + >( + () => + partition(peekedParticipants, (participant: PeekedParticipantType) => + Boolean(participant.titleNoDefault) + ), + [peekedParticipants] + ); + let subtitle: string; if (ringMode === RingMode.IsRinging) { subtitle = i18n('icu:outgoingCallRinging'); } else if (isCallFull) { subtitle = i18n('icu:calling__call-is-full'); } else if (peekedParticipants.length) { - // It should be rare to see yourself in this list, but it's possible if (1) you rejoin - // quickly, causing the server to return stale state (2) you have joined on another - // device. - let hasYou = false; - const participantNames = peekedParticipants.map(participant => { - if (participant.serviceId === me.serviceId) { - hasYou = true; - return i18n('icu:you'); + if (unknownParticipants.length > 0) { + subtitle = i18n( + 'icu:calling__pre-call-info--only-unknown-contacts-in-call', + { + count: peekedParticipants.length, + } + ); + } else { + // It should be rare to see yourself in this list, but it's possible if (1) you + // rejoin quickly, causing the server to return stale state (2) you have joined on + // another device. + let hasYou = false; + const participantNames = visibleParticipants.map(participant => { + if (participant.serviceId === me.serviceId) { + hasYou = true; + return i18n('icu:you'); + } + return getParticipantName(participant); + }); + switch (participantNames.length) { + case 1: + subtitle = hasYou + ? i18n('icu:calling__pre-call-info--another-device-in-call') + : i18n('icu:calling__pre-call-info--1-person-in-call', { + first: participantNames[0], + }); + break; + case 2: + subtitle = i18n('icu:calling__pre-call-info--2-people-in-call', { + first: participantNames[0], + second: participantNames[1], + }); + break; + case 3: + subtitle = i18n('icu:calling__pre-call-info--3-people-in-call', { + first: participantNames[0], + second: participantNames[1], + third: participantNames[2], + }); + break; + default: + subtitle = i18n('icu:calling__pre-call-info--many-people-in-call', { + first: participantNames[0], + second: participantNames[1], + others: participantNames.length - 2, + }); + break; } - return getParticipantName(participant); - }); - - switch (participantNames.length) { - case 1: - subtitle = hasYou - ? i18n('icu:calling__pre-call-info--another-device-in-call') - : i18n('icu:calling__pre-call-info--1-person-in-call', { - first: participantNames[0], - }); - break; - case 2: - subtitle = i18n('icu:calling__pre-call-info--2-people-in-call', { - first: participantNames[0], - second: participantNames[1], - }); - break; - case 3: - subtitle = i18n('icu:calling__pre-call-info--3-people-in-call', { - first: participantNames[0], - second: participantNames[1], - third: participantNames[2], - }); - break; - default: - subtitle = i18n('icu:calling__pre-call-info--many-people-in-call', { - first: participantNames[0], - second: participantNames[1], - others: participantNames.length - 2, - }); - break; } } else { let memberNames: Array;