From 26140ee3d68f891736d60521eeb819142e8eec83 Mon Sep 17 00:00:00 2001 From: ayumi-signal <143036029+ayumi-signal@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:21:33 -0700 Subject: [PATCH] Update call strings --- _locales/en/messages.json | 62 ++++--- stylesheets/components/CallsTab.scss | 3 +- ts/components/CallsList.tsx | 9 +- .../conversation/CallingNotification.tsx | 24 ++- .../CallHistoryGroupPanelSection.tsx | 29 ++-- ts/test-both/util/callingNotification_test.ts | 158 +++++++++++++++++- ts/util/callingNotification.ts | 57 ++++++- 7 files changed, 289 insertions(+), 53 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 3171e2f3896..32e43cbfd9d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -469,7 +469,7 @@ }, "icu:ContactListItem__menu__audio-call": { "messageformat": "Voice call", - "description": "Shown in a context menu for a contact, allows the user to start the audio call with the contact" + "description": "Shown in a context menu for a contact, allows the user to start the voice call with the contact" }, "icu:ContactListItem__menu__video-call": { "messageformat": "Video call", @@ -1598,19 +1598,19 @@ "description": "Header for calling options on the settings screen" }, "icu:calling__call-back": { - "messageformat": "Call Back", + "messageformat": "Call back", "description": "Button to call someone back" }, "icu:calling__call-again": { - "messageformat": "Call Again", + "messageformat": "Call again", "description": "Button to call someone again" }, "icu:calling__join": { - "messageformat": "Join Call", + "messageformat": "Join call", "description": "Button label in the call lobby for joining a call" }, "icu:calling__return": { - "messageformat": "Return to Call", + "messageformat": "Return to call", "description": "Button label in the call lobby for returning to a call" }, "icu:calling__lobby-automatically-muted-because-there-are-a-lot-of-people": { @@ -3496,11 +3496,11 @@ "description": "Shown in tooltip for the button to decline a call (audio or video)" }, "icu:declinedIncomingAudioCall": { - "messageformat": "You declined a voice call", + "messageformat": "Declined voice call", "description": "Shown in conversation history when you declined an incoming voice call" }, "icu:declinedIncomingVideoCall": { - "messageformat": "You declined a video call", + "messageformat": "Declined video call", "description": "Shown in conversation history when you declined an incoming video call" }, "icu:acceptedIncomingAudioCall": { @@ -3544,11 +3544,11 @@ "description": "Shown in a notification body when Signal is minimized to tray" }, "icu:incomingAudioCall": { - "messageformat": "Incoming voice call...", + "messageformat": "Incoming voice call", "description": "Shown in both the incoming call bar and notification for an incoming voice call" }, "icu:incomingVideoCall": { - "messageformat": "Incoming video call...", + "messageformat": "Incoming video call", "description": "Shown in both the incoming call bar and notification for an incoming video call" }, "icu:outgoingAudioCall": { @@ -3608,8 +3608,8 @@ "description": "Shown in the call screen and lobby for group calls to specify the number of members in the call or in the group. Count is at always at least 1." }, "icu:CallControls__InfoDisplay--audio-call": { - "messageformat": "Audio call", - "description": "Shown in the call lobby for a direct 1:1 call when the caller's video is disabled, to specify that an audio call will be placed when clicking the Start button." + "messageformat": "Voice call", + "description": "Shown in the call lobby for a direct 1:1 call when the caller's video is disabled, to specify that a voice call will be placed when clicking the Start button." }, "icu:CallControls__InfoDisplay--adhoc-call": { "messageformat": "Call link", @@ -3692,19 +3692,19 @@ "description": "Title for participants list toggle" }, "icu:calling__call-notification__ended": { - "messageformat": "The group call has ended", + "messageformat": "The video call has ended", "description": "Notification message when a group call has ended" }, "icu:calling__call-notification__started-by-someone": { - "messageformat": "A group call was started", + "messageformat": "A video call was started", "description": "Notification message when a group call has started, but we don't know who started it" }, "icu:calling__call-notification__started-by-you": { - "messageformat": "You started a group call", + "messageformat": "You started a video call", "description": "Notification message when a group call has started by you" }, "icu:calling__call-notification__started": { - "messageformat": "{name} started a group call", + "messageformat": "{name} started a video call", "description": "Notification message when a group call has started" }, "icu:calling__in-another-call-tooltip": { @@ -4765,7 +4765,7 @@ }, "icu:ContactModal--already-in-call": { "messageformat": "You are already in a call", - "description": "Tooltip text for video or audio call button in Contact Details modal" + "description": "Tooltip text for video or voice call button in Contact Details modal" }, "icu:showChatColorEditor": { "messageformat": "Chat color", @@ -7225,9 +7225,13 @@ "messageformat": "Missed", "description": "Calls Tab > Calls List > Call Item > Call Status > When call was missed" }, + "icu:CallsList__ItemCallInfo--Declined": { + "messageformat": "Declined", + "description": "Calls Tab > Calls List > Call Item > Call Status > When an incoming call was declined" + }, "icu:CallsList__ItemCallInfo--GroupCall": { "messageformat": "Group call", - "description": "Calls Tab > Calls List > Call Item > Call Status > When group call is in its default state" + "description": "(Deleted 2024/07/26) Calls Tab > Calls List > Call Item > Call Status > When group call is in its default state" }, "icu:CallsList__ItemCallInfo--CallLink": { "messageformat": "Call link", @@ -7267,24 +7271,40 @@ }, "icu:CallHistory__Description--Default": { "messageformat": "{direction, select, Outgoing {Outgoing} other {Incoming}} {type, select, Audio {voice} Video {video} Group {group} other {}} call", - "description": "Call History > Short description of call > When call was not missed or declined (generally accepted)" + "description": "(Deleted 2024/07/30) Call History > Short description of call > When call was not missed or declined (generally accepted)" }, "icu:CallHistory__Description--Missed": { "messageformat": "Missed {type, select, Audio {voice} Video {video} Group {group} other {}} call", - "description": "Call History > Short description of call > When incoming call was missed" + "description": "(Deleted 2024/07/30) Call History > Short description of call > When incoming call was missed" }, "icu:CallHistory__Description--Unanswered": { "messageformat": "Unanswered {type, select, Audio {voice} Video {video} Group {group} other {}} call", - "description": "Call History > Short description of call > When outgoing call was unanswered" + "description": "(Deleted 2024/07/30) Call History > Short description of call > When outgoing call was unanswered" }, "icu:CallHistory__Description--Declined": { "messageformat": "Declined {type, select, Audio {voice} Video {video} Group {group} other {}} call", - "description": "Call History > Short description of call > When call was declined" + "description": "(Deleted 2024/07/30) Call History > Short description of call > When call was declined" }, "icu:CallHistory__Description--Adhoc": { "messageformat": "Call link", "description": "Call History > Short description of call > When you joined a call link call" }, + "icu:CallHistory__DescriptionVideoCall--Default": { + "messageformat": "{direction, select, Outgoing {Outgoing} other {Incoming}} video call", + "description": "Call History > Short description of call > When group or direct video call was not missed or declined (generally accepted)" + }, + "icu:CallHistory__DescriptionVideoCall--Missed": { + "messageformat": "Missed video call", + "description": "Call History > Short description of call > When incoming group or direct video call was missed" + }, + "icu:CallHistory__DescriptionVideoCall--Unanswered": { + "messageformat": "Unanswered video call", + "description": "Call History > Short description of call > When outgoing group or direct video call was unanswered" + }, + "icu:CallHistory__DescriptionVideoCall--Declined": { + "messageformat": "Declined video call", + "description": "Call History > Short description of call > When group or direct video call was declined" + }, "icu:CallLinkDetails__Join": { "messageformat": "Join", "description": "Call History > Call Link Details > Join Button" diff --git a/stylesheets/components/CallsTab.scss b/stylesheets/components/CallsTab.scss index 4ae2d164067..873bf5b06eb 100644 --- a/stylesheets/components/CallsTab.scss +++ b/stylesheets/components/CallsTab.scss @@ -235,7 +235,8 @@ } // Override .ListTile__subtitle so ellipsis is correct color -.CallsList__Item--missed .ListTile__subtitle { +.CallsList__Item--missed .ListTile__subtitle, +.CallsList__Item--declined .ListTile__subtitle { // Need to override the themed selector specificity of .ListTile__subtitle @include light-theme { color: $color-accent-red; diff --git a/ts/components/CallsList.tsx b/ts/components/CallsList.tsx index f8dab56333c..1a02f4852e5 100644 --- a/ts/components/CallsList.tsx +++ b/ts/components/CallsList.tsx @@ -758,14 +758,18 @@ export function CallsList({ item.direction === CallDirection.Incoming && (item.status === DirectCallStatus.Missed || item.status === GroupCallStatus.Missed); + const wasDeclined = + item.direction === CallDirection.Incoming && + (item.status === DirectCallStatus.Declined || + item.status === GroupCallStatus.Declined); let statusText; if (wasMissed) { statusText = i18n('icu:CallsList__ItemCallInfo--Missed'); + } else if (wasDeclined) { + statusText = i18n('icu:CallsList__ItemCallInfo--Declined'); } else if (isAdhoc) { statusText = i18n('icu:CallsList__ItemCallInfo--CallLink'); - } else if (item.type === CallType.Group) { - statusText = i18n('icu:CallsList__ItemCallInfo--GroupCall'); } else if (item.direction === CallDirection.Outgoing) { statusText = i18n('icu:CallsList__ItemCallInfo--Outgoing'); } else if (item.direction === CallDirection.Incoming) { @@ -819,6 +823,7 @@ export function CallsList({ className={classNames('CallsList__Item', { 'CallsList__Item--selected': isSelected, 'CallsList__Item--missed': wasMissed, + 'CallsList__Item--declined': wasDeclined, })} > void; @@ -105,7 +107,9 @@ export const CallingNotification: React.FC = React.memo( icon={icon} kind={ status === DirectCallStatus.Missed || - status === GroupCallStatus.Missed + status === GroupCallStatus.Missed || + status === DirectCallStatus.Declined || + status === GroupCallStatus.Declined ? SystemMessageKind.Danger : SystemMessageKind.Normal } @@ -188,9 +192,21 @@ function renderCallingNotificationButton( } case CallMode.Group: { if (props.groupCallEnded) { - return null; - } - if (props.activeConversationId != null) { + const { direction, status, timestamp } = props.callHistory; + if ( + (direction === CallDirection.Incoming && + (status === GroupCallStatus.Declined || + status === GroupCallStatus.Missed)) || + isMoreRecentThan(timestamp, 5 * MINUTE) + ) { + buttonText = i18n('icu:calling__call-back'); + onClick = () => { + onOutgoingVideoCallInConversation(conversationId); + }; + } else { + return null; + } + } else if (props.activeConversationId != null) { if (props.activeConversationId === conversationId) { buttonText = i18n('icu:calling__return'); onClick = returnToActiveCall; diff --git a/ts/components/conversation/conversation-details/CallHistoryGroupPanelSection.tsx b/ts/components/conversation/conversation-details/CallHistoryGroupPanelSection.tsx index c24cfac6906..b8b4133db98 100644 --- a/ts/components/conversation/conversation-details/CallHistoryGroupPanelSection.tsx +++ b/ts/components/conversation/conversation-details/CallHistoryGroupPanelSection.tsx @@ -13,6 +13,7 @@ import { import type { LocalizerType } from '../../../types/I18N'; import { formatDate, formatTime } from '../../../util/timestamp'; import { PanelSection } from './PanelSection'; +import { getDirectCallNotificationText } from '../../../util/callingNotification'; function describeCallHistory( i18n: LocalizerType, @@ -24,19 +25,27 @@ function describeCallHistory( return i18n('icu:CallHistory__Description--Adhoc'); } - if (status === DirectCallStatus.Missed || status === GroupCallStatus.Missed) { - if (direction === CallDirection.Incoming) { - return i18n('icu:CallHistory__Description--Missed', { type }); - } - return i18n('icu:CallHistory__Description--Unanswered', { type }); - } if ( - status === DirectCallStatus.Declined || - status === GroupCallStatus.Declined + (type === CallType.Audio || type === CallType.Video) && + (status === DirectCallStatus.Accepted || + status === DirectCallStatus.Declined || + status === DirectCallStatus.Deleted || + status === DirectCallStatus.Missed || + status === DirectCallStatus.Pending) ) { - return i18n('icu:CallHistory__Description--Declined', { type }); + return getDirectCallNotificationText(direction, type, status, i18n); } - return i18n('icu:CallHistory__Description--Default', { type, direction }); + + if (status === GroupCallStatus.Missed) { + if (direction === CallDirection.Incoming) { + return i18n('icu:CallHistory__DescriptionVideoCall--Missed'); + } + return i18n('icu:CallHistory__DescriptionVideoCall--Unanswered'); + } + if (status === GroupCallStatus.Declined) { + return i18n('icu:CallHistory__DescriptionVideoCall--Declined'); + } + return i18n('icu:CallHistory__DescriptionVideoCall--Default', { direction }); } export type CallHistoryPanelSectionProps = Readonly<{ diff --git a/ts/test-both/util/callingNotification_test.ts b/ts/test-both/util/callingNotification_test.ts index 918dce9f1a4..5e32cc5d28e 100644 --- a/ts/test-both/util/callingNotification_test.ts +++ b/ts/test-both/util/callingNotification_test.ts @@ -16,6 +16,7 @@ import { GroupCallStatus, } from '../../types/CallDisposition'; import { getPeerIdFromConversation } from '../../util/callDisposition'; +import { HOUR } from '../../util/durations'; describe('calling notification helpers', () => { const i18n = setupI18n('en', enMessages); @@ -23,7 +24,7 @@ describe('calling notification helpers', () => { describe('getCallingNotificationText', () => { // Direct call behavior is not tested here. - it('says that the call has ended', () => { + it('says that the incoming call has ended', () => { const callCreator = getDefaultConversation(); assert.strictEqual( getCallingNotificationText( @@ -36,6 +37,122 @@ describe('calling notification helpers', () => { type: CallType.Group, direction: CallDirection.Incoming, timestamp: Date.now(), + status: GroupCallStatus.Joined, + }, + callCreator, + activeConversationId: null, + groupCallEnded: true, + deviceCount: 1, + maxDevices: 23, + isSelectMode: false, + isTargeted: false, + }, + i18n + ), + 'The video call has ended' + ); + }); + + it('says that the outgoing call has ended', () => { + const callCreator = getDefaultConversation(); + assert.strictEqual( + getCallingNotificationText( + { + callHistory: { + callId: '123', + peerId: getPeerIdFromConversation(getDefaultGroup()), + ringerId: callCreator.serviceId ?? null, + mode: CallMode.Group, + type: CallType.Group, + direction: CallDirection.Outgoing, + timestamp: Date.now(), + status: GroupCallStatus.Joined, + }, + callCreator, + activeConversationId: null, + groupCallEnded: true, + deviceCount: 1, + maxDevices: 23, + isSelectMode: false, + isTargeted: false, + }, + i18n + ), + 'The video call has ended' + ); + }); + + it('says declined incoming calls', () => { + const callCreator = getDefaultConversation(); + assert.strictEqual( + getCallingNotificationText( + { + callHistory: { + callId: '123', + peerId: getPeerIdFromConversation(getDefaultGroup()), + ringerId: callCreator.serviceId ?? null, + mode: CallMode.Group, + type: CallType.Group, + direction: CallDirection.Incoming, + timestamp: Date.now(), + status: GroupCallStatus.Declined, + }, + callCreator, + activeConversationId: null, + groupCallEnded: true, + deviceCount: 1, + maxDevices: 23, + isSelectMode: false, + isTargeted: false, + }, + i18n + ), + 'Declined video call' + ); + }); + + it('says older ended incoming calls', () => { + const callCreator = getDefaultConversation(); + assert.strictEqual( + getCallingNotificationText( + { + callHistory: { + callId: '123', + peerId: getPeerIdFromConversation(getDefaultGroup()), + ringerId: callCreator.serviceId ?? null, + mode: CallMode.Group, + type: CallType.Group, + direction: CallDirection.Incoming, + timestamp: Date.now() - HOUR, + status: GroupCallStatus.Joined, + }, + callCreator, + activeConversationId: null, + groupCallEnded: true, + deviceCount: 1, + maxDevices: 23, + isSelectMode: false, + isTargeted: false, + }, + i18n + ), + 'Incoming video call' + ); + }); + + it('says older ended incoming missed calls', () => { + const callCreator = getDefaultConversation(); + assert.strictEqual( + getCallingNotificationText( + { + callHistory: { + callId: '123', + peerId: getPeerIdFromConversation(getDefaultGroup()), + ringerId: callCreator.serviceId ?? null, + mode: CallMode.Group, + type: CallType.Group, + direction: CallDirection.Incoming, + timestamp: Date.now() - HOUR, status: GroupCallStatus.Missed, }, callCreator, @@ -48,7 +165,36 @@ describe('calling notification helpers', () => { }, i18n ), - 'The group call has ended' + 'Missed video call' + ); + }); + + it('says older ended outgoing calls', () => { + const callCreator = getDefaultConversation(); + assert.strictEqual( + getCallingNotificationText( + { + callHistory: { + callId: '123', + peerId: getPeerIdFromConversation(getDefaultGroup()), + ringerId: callCreator.serviceId ?? null, + mode: CallMode.Group, + type: CallType.Group, + direction: CallDirection.Outgoing, + timestamp: Date.now() - HOUR, + status: GroupCallStatus.Joined, + }, + callCreator, + activeConversationId: null, + groupCallEnded: true, + deviceCount: 1, + maxDevices: 23, + isSelectMode: false, + isTargeted: false, + }, + i18n + ), + 'Outgoing video call' ); }); @@ -79,7 +225,7 @@ describe('calling notification helpers', () => { }, i18n ), - 'Luigi started a group call' + 'Luigi started a video call' ); }); @@ -111,7 +257,7 @@ describe('calling notification helpers', () => { }, i18n ), - 'Luigi Mario started a group call' + 'Luigi Mario started a video call' ); }); @@ -142,7 +288,7 @@ describe('calling notification helpers', () => { }, i18n ), - 'You started a group call' + 'You started a video call' ); }); @@ -170,7 +316,7 @@ describe('calling notification helpers', () => { }, i18n ), - 'A group call was started' + 'A video call was started' ); }); }); diff --git a/ts/util/callingNotification.ts b/ts/util/callingNotification.ts index 8ca95a4a5a4..cdfa14e962d 100644 --- a/ts/util/callingNotification.ts +++ b/ts/util/callingNotification.ts @@ -10,9 +10,12 @@ import { DirectCallStatus, type CallHistoryDetails, CallType, + GroupCallStatus, } from '../types/CallDisposition'; import type { ConversationType } from '../state/ducks/conversations'; import { strictAssert } from './assert'; +import { isMoreRecentThan } from './timestamp'; +import { MINUTE } from './durations'; export type CallingNotificationType = Readonly<{ // In some older calls, we don't have a call id, this hardens against that. @@ -26,7 +29,7 @@ export type CallingNotificationType = Readonly<{ isTargeted: boolean; }>; -function getDirectCallNotificationText( +export function getDirectCallNotificationText( callDirection: CallDirection, callType: CallType, callStatus: DirectCallStatus, @@ -85,14 +88,41 @@ function getDirectCallNotificationText( throw missingCaseError(callStatus); } -function getGroupCallNotificationText( - groupCallEnded: boolean, - creator: ConversationType | null, - i18n: LocalizerType -): string { +function getGroupCallNotificationText({ + groupCallEnded, + creator, + callHistory, + i18n, +}: { + groupCallEnded: boolean; + creator: ConversationType | null; + callHistory: CallHistoryDetails; + i18n: LocalizerType; +}): string { if (groupCallEnded) { - return i18n('icu:calling__call-notification__ended'); + const { direction, status, timestamp } = callHistory; + if (direction === CallDirection.Incoming) { + if (status === GroupCallStatus.Declined) { + return i18n('icu:CallHistory__DescriptionVideoCall--Declined'); + } + if (status === GroupCallStatus.Missed) { + return i18n('icu:CallHistory__DescriptionVideoCall--Missed'); + } + if (isMoreRecentThan(timestamp, 5 * MINUTE)) { + return i18n('icu:calling__call-notification__ended'); + } + return i18n('icu:acceptedIncomingVideoCall'); + } + + // Outgoing ended group calls + if (isMoreRecentThan(timestamp, 5 * MINUTE)) { + return i18n('icu:calling__call-notification__ended'); + } + return i18n('icu:acceptedOutgoingVideoCall'); } + + // TODO: Active call with participants DESKTOP-7439 + if (creator == null) { return i18n('icu:calling__call-notification__started-by-someone'); } @@ -108,7 +138,11 @@ export function getCallingNotificationText( callingNotification: CallingNotificationType, i18n: LocalizerType ): string | null { - const { callHistory, callCreator, groupCallEnded } = callingNotification; + const { + callHistory, + callCreator: creator, + groupCallEnded, + } = callingNotification; if (callHistory == null) { return null; } @@ -126,7 +160,12 @@ export function getCallingNotificationText( groupCallEnded != null, 'getCallingNotificationText: groupCallEnded shouldnt be null for a group call' ); - return getGroupCallNotificationText(groupCallEnded, callCreator, i18n); + return getGroupCallNotificationText({ + groupCallEnded, + creator, + callHistory, + i18n, + }); } if (callHistory.mode === CallMode.Adhoc) { return null;