Introduce TitleTransition notification
This commit is contained in:
parent
09b5e6ef50
commit
3469a748fb
16 changed files with 336 additions and 51 deletions
|
@ -1541,6 +1541,10 @@
|
|||
"messageformat": "{phoneNumber} belongs to {conversationTitle}",
|
||||
"description": "Shown when we've discovered a phone number for a contact you've been communicating with, but you have no shared groups."
|
||||
},
|
||||
"icu:TitleTransition--notification": {
|
||||
"messageformat": "You started this chat with {oldTitle}",
|
||||
"description": "Shown when we've received profile information for a contact we have been messaging by username/phone number"
|
||||
},
|
||||
"icu:quoteThumbnailAlt": {
|
||||
"messageformat": "Thumbnail of image from quoted message",
|
||||
"description": "Used in alt tag of thumbnail images inside of an embedded message quote"
|
||||
|
|
4
images/icons/v3/thread/thread-compact.svg
Normal file
4
images/icons/v3/thread/thread-compact.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5.33335 2.31672C3.4832 2.31672 1.98335 3.81657 1.98335 5.66672C1.98335 6.48907 2.27882 7.24071 2.77013 7.82381C2.93242 8.01642 3.02713 8.27476 3.00398 8.55045L2.90267 9.75728L3.85216 9.09739C4.08958 8.93239 4.37443 8.88846 4.63132 8.94318C4.72496 8.96313 4.82014 8.97918 4.91669 8.99113L4.91668 9.00006C4.91668 9.45039 4.96845 9.88864 5.06637 10.3092C4.86977 10.298 4.67621 10.2746 4.48648 10.2397L3.092 11.2088C2.40877 11.6837 1.48132 11.1482 1.55093 10.3191L1.69806 8.56648C1.06332 7.7716 0.68335 6.76276 0.68335 5.66672C0.68335 3.0986 2.76523 1.01672 5.33335 1.01672C7.07529 1.01672 8.59352 1.97456 9.39008 3.39227C8.95168 3.49166 8.53229 3.64113 8.13826 3.83436C7.54002 2.92046 6.50719 2.31672 5.33335 2.31672Z" fill="black"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.6667 4.35C13.2349 4.35 15.3167 6.43188 15.3167 9C15.3167 10.0917 14.9397 11.097 14.3095 11.8904L14.461 13.6955C14.5306 14.5246 13.6032 15.06 12.9199 14.5852L11.4737 13.5801C11.2112 13.6261 10.9415 13.65 10.6667 13.65C8.09862 13.65 6.01675 11.5681 6.01675 9C6.01675 6.43188 8.09862 4.35 10.6667 4.35ZM14.0167 9C14.0167 7.14985 12.5169 5.65 10.6667 5.65C8.81659 5.65 7.31675 7.14985 7.31675 9C7.31675 10.8502 8.81659 12.35 10.6667 12.35C10.8977 12.35 11.1225 12.3267 11.3392 12.2826C11.5936 12.2308 11.8747 12.2757 12.1094 12.4387L13.1093 13.1337L13.0036 11.8748C12.9805 11.6001 13.0745 11.3426 13.2357 11.1502C13.7236 10.5681 14.0167 9.81914 14.0167 9Z" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -166,6 +166,12 @@
|
|||
@include system-message-icon('../images/icons/v3/refresh/refresh.svg');
|
||||
}
|
||||
|
||||
&--icon-thread::before {
|
||||
@include system-message-icon(
|
||||
'../images/icons/v3/thread/thread-compact.svg'
|
||||
);
|
||||
}
|
||||
|
||||
&--icon-timer::before {
|
||||
@include system-message-icon(
|
||||
'../images/icons/v3/timer/timer-compact.svg'
|
||||
|
|
|
@ -720,6 +720,7 @@ export class ConversationController {
|
|||
(targetOldServiceIds.pni !== pni ||
|
||||
(aci && targetOldServiceIds.aci !== aci))
|
||||
) {
|
||||
targetConversation.unset('needsTitleTransition');
|
||||
mergePromises.push(
|
||||
targetConversation.addPhoneNumberDiscoveryIfNeeded(
|
||||
targetOldServiceIds.pni
|
||||
|
@ -1056,6 +1057,8 @@ export class ConversationController {
|
|||
}
|
||||
current.set('active_at', activeAt);
|
||||
|
||||
const currentHadMessages = (current.get('messageCount') ?? 0) > 0;
|
||||
|
||||
const dataToCopy: Partial<ConversationAttributesType> = pick(
|
||||
obsolete.attributes,
|
||||
[
|
||||
|
@ -1067,6 +1070,7 @@ export class ConversationController {
|
|||
'draftTimestamp',
|
||||
'messageCount',
|
||||
'messageRequestResponseType',
|
||||
'needsTitleTransition',
|
||||
'profileSharing',
|
||||
'quotedMessageId',
|
||||
'sentMessageCount',
|
||||
|
@ -1196,7 +1200,15 @@ export class ConversationController {
|
|||
const titleIsUseful = Boolean(
|
||||
obsoleteTitleInfo && getTitleNoDefault(obsoleteTitleInfo)
|
||||
);
|
||||
if (obsoleteTitleInfo && titleIsUseful && obsoleteHadMessages) {
|
||||
// If both conversations had messages - add merge
|
||||
if (
|
||||
titleIsUseful &&
|
||||
conversationType === 'private' &&
|
||||
currentHadMessages &&
|
||||
obsoleteHadMessages
|
||||
) {
|
||||
assertDev(obsoleteTitleInfo, 'part of titleIsUseful boolean');
|
||||
|
||||
drop(current.addConversationMerge(obsoleteTitleInfo));
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,39 @@ export enum SystemMessageKind {
|
|||
}
|
||||
|
||||
export type PropsType = {
|
||||
icon: string;
|
||||
icon:
|
||||
| 'audio-incoming'
|
||||
| 'audio-missed'
|
||||
| 'audio-outgoing'
|
||||
| 'group'
|
||||
| 'group-access'
|
||||
| 'group-add'
|
||||
| 'group-approved'
|
||||
| 'group-avatar'
|
||||
| 'group-decline'
|
||||
| 'group-edit'
|
||||
| 'group-leave'
|
||||
| 'group-remove'
|
||||
| 'group-summary'
|
||||
| 'info'
|
||||
| 'phone'
|
||||
| 'profile'
|
||||
| 'safety-number'
|
||||
| 'session-refresh'
|
||||
| 'thread'
|
||||
| 'timer'
|
||||
| 'timer-disabled'
|
||||
| 'unsupported'
|
||||
| 'unsupported--can-process'
|
||||
| 'verified'
|
||||
| 'verified-not'
|
||||
| 'video'
|
||||
| 'video-incoming'
|
||||
| 'video-missed'
|
||||
| 'video-outgoing'
|
||||
| 'warning'
|
||||
| 'payment-event'
|
||||
| 'merge';
|
||||
contents: ReactNode;
|
||||
button?: ReactNode;
|
||||
kind?: SystemMessageKind;
|
||||
|
|
|
@ -193,6 +193,12 @@ export function Notification(): JSX.Element {
|
|||
timestamp: Date.now(),
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'titleTransitionNotification',
|
||||
data: {
|
||||
oldTitle: 'alice.01',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'callHistory',
|
||||
data: {
|
||||
|
|
|
@ -20,6 +20,8 @@ import type { PropsDataType as DeliveryIssueProps } from './DeliveryIssueNotific
|
|||
import { DeliveryIssueNotification } from './DeliveryIssueNotification';
|
||||
import type { PropsData as ChangeNumberNotificationProps } from './ChangeNumberNotification';
|
||||
import { ChangeNumberNotification } from './ChangeNumberNotification';
|
||||
import type { PropsData as TitleTransitionNotificationProps } from './TitleTransitionNotification';
|
||||
import { TitleTransitionNotification } from './TitleTransitionNotification';
|
||||
import type { CallingNotificationType } from '../../util/callingNotification';
|
||||
import { InlineNotificationWrapper } from './InlineNotificationWrapper';
|
||||
import type { PropsData as UnsupportedMessageProps } from './UnsupportedMessage';
|
||||
|
@ -91,6 +93,10 @@ type ChangeNumberNotificationType = {
|
|||
type: 'changeNumberNotification';
|
||||
data: ChangeNumberNotificationProps;
|
||||
};
|
||||
type TitleTransitionNotificationType = {
|
||||
type: 'titleTransitionNotification';
|
||||
data: TitleTransitionNotificationProps;
|
||||
};
|
||||
type SafetyNumberNotificationType = {
|
||||
type: 'safetyNumberNotification';
|
||||
data: SafetyNumberNotificationProps;
|
||||
|
@ -148,6 +154,7 @@ export type TimelineItemType = (
|
|||
| SafetyNumberNotificationType
|
||||
| TimerNotificationType
|
||||
| UniversalTimerNotificationType
|
||||
| TitleTransitionNotificationType
|
||||
| ContactRemovedNotificationType
|
||||
| UnsupportedMessageType
|
||||
| VerificationNotificationType
|
||||
|
@ -296,6 +303,14 @@ export const TimelineItem = memo(function TimelineItem({
|
|||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
} else if (item.type === 'titleTransitionNotification') {
|
||||
notification = (
|
||||
<TitleTransitionNotification
|
||||
{...reducedProps}
|
||||
{...item.data}
|
||||
i18n={i18n}
|
||||
/>
|
||||
);
|
||||
} else if (item.type === 'safetyNumberNotification') {
|
||||
notification = (
|
||||
<SafetyNumberNotification
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import * as React from 'react';
|
||||
import type { Meta } from '@storybook/react';
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
import type { Props } from './TitleTransitionNotification';
|
||||
import { TitleTransitionNotification } from './TitleTransitionNotification';
|
||||
|
||||
export default {
|
||||
title: 'Components/Conversation/TitleTransitionNotification',
|
||||
} satisfies Meta<Props>;
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
export function Default(): JSX.Element {
|
||||
return <TitleTransitionNotification oldTitle="alice.01" i18n={i18n} />;
|
||||
}
|
39
ts/components/conversation/TitleTransitionNotification.tsx
Normal file
39
ts/components/conversation/TitleTransitionNotification.tsx
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2024 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { Intl } from '../Intl';
|
||||
|
||||
import { SystemMessage } from './SystemMessage';
|
||||
import { UserText } from '../UserText';
|
||||
|
||||
export type PropsData = {
|
||||
oldTitle: string;
|
||||
};
|
||||
|
||||
export type PropsHousekeeping = {
|
||||
i18n: LocalizerType;
|
||||
};
|
||||
|
||||
export type Props = PropsData & PropsHousekeeping;
|
||||
|
||||
export function TitleTransitionNotification(props: Props): JSX.Element {
|
||||
const { i18n, oldTitle } = props;
|
||||
|
||||
return (
|
||||
<SystemMessage
|
||||
contents={
|
||||
<Intl
|
||||
id="icu:TitleTransition--notification"
|
||||
components={{
|
||||
oldTitle: <UserText text={oldTitle} />,
|
||||
}}
|
||||
i18n={i18n}
|
||||
/>
|
||||
}
|
||||
icon="thread"
|
||||
/>
|
||||
);
|
||||
}
|
5
ts/model-types.d.ts
vendored
5
ts/model-types.d.ts
vendored
|
@ -191,6 +191,7 @@ export type MessageAttributesType = {
|
|||
| 'timer-notification'
|
||||
| 'universal-timer-notification'
|
||||
| 'contact-removed-notification'
|
||||
| 'title-transition-notification'
|
||||
| 'verified-change';
|
||||
body?: string;
|
||||
attachments?: Array<AttachmentType>;
|
||||
|
@ -225,6 +226,9 @@ export type MessageAttributesType = {
|
|||
conversationMerge?: {
|
||||
renderInfo: ConversationRenderInfoType;
|
||||
};
|
||||
titleTransition?: {
|
||||
renderInfo: ConversationRenderInfoType;
|
||||
};
|
||||
|
||||
// Legacy fields for timer update notification only
|
||||
flags?: number;
|
||||
|
@ -338,6 +342,7 @@ export type ConversationAttributesType = {
|
|||
profileKeyCredential?: string | null;
|
||||
profileKeyCredentialExpiration?: number | null;
|
||||
lastProfile?: ConversationLastProfileType;
|
||||
needsTitleTransition?: boolean;
|
||||
quotedMessageId?: string | null;
|
||||
sealedSender?: unknown;
|
||||
sentMessageCount?: number;
|
||||
|
|
|
@ -95,6 +95,8 @@ import {
|
|||
getProfileName,
|
||||
getTitle,
|
||||
getTitleNoDefault,
|
||||
hasNumberTitle,
|
||||
hasUsernameTitle,
|
||||
canHaveUsername,
|
||||
} from '../util/getTitle';
|
||||
import { markConversationRead } from '../util/markConversationRead';
|
||||
|
@ -3717,10 +3719,13 @@ export class ConversationModel extends window.Backbone
|
|||
};
|
||||
|
||||
const isEditMessage = Boolean(message.get('editHistory'));
|
||||
const needsTitleTransition =
|
||||
hasNumberTitle(this.attributes) || hasUsernameTitle(this.attributes);
|
||||
|
||||
this.set({
|
||||
...draftProperties,
|
||||
...(enabledProfileSharing ? { profileSharing: true } : {}),
|
||||
...(needsTitleTransition ? { needsTitleTransition: true } : {}),
|
||||
...(dontAddMessage
|
||||
? {}
|
||||
: this.incrementSentMessageCount({ dry: true })),
|
||||
|
@ -4013,17 +4018,38 @@ export class ConversationModel extends window.Backbone
|
|||
const ourConversationId =
|
||||
window.ConversationController.getOurConversationId();
|
||||
|
||||
const oldUsername = this.get('username');
|
||||
|
||||
// Clear username once we have other information about the contact
|
||||
if (
|
||||
canHaveUsername(this.attributes, ourConversationId) ||
|
||||
!this.get('username')
|
||||
) {
|
||||
if (canHaveUsername(this.attributes, ourConversationId) || !oldUsername) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(`maybeClearUsername(${this.idForLogging()}): clearing username`);
|
||||
|
||||
this.unset('username');
|
||||
|
||||
if (this.get('needsTitleTransition') && getProfileName(this.attributes)) {
|
||||
log.info(
|
||||
`maybeClearUsername(${this.idForLogging()}): adding a notification`
|
||||
);
|
||||
const { type, e164, username } = this.attributes;
|
||||
|
||||
this.unset('needsTitleTransition');
|
||||
|
||||
await this.addNotification('title-transition-notification', {
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Unseen,
|
||||
titleTransition: {
|
||||
renderInfo: {
|
||||
type,
|
||||
e164,
|
||||
username,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
window.Signal.Data.updateConversation(this.attributes);
|
||||
this.captureChange('clearUsername');
|
||||
}
|
||||
|
@ -4684,37 +4710,63 @@ export class ConversationModel extends window.Backbone
|
|||
const oldProfileKey = this.get('profileKey');
|
||||
|
||||
// profileKey is a string so we can compare it directly
|
||||
if (oldProfileKey !== profileKey) {
|
||||
log.info(
|
||||
`Setting sealedSender to UNKNOWN for conversation ${this.idForLogging()}`
|
||||
);
|
||||
this.set({
|
||||
profileKeyCredential: null,
|
||||
profileKeyCredentialExpiration: null,
|
||||
accessKey: null,
|
||||
sealedSender: SEALED_SENDER.UNKNOWN,
|
||||
});
|
||||
|
||||
// Don't trigger immediate profile fetches when syncing to remote storage
|
||||
this.set({ profileKey }, { silent: viaStorageServiceSync });
|
||||
|
||||
// If our profile key was cleared above, we don't tell our linked devices about it.
|
||||
// We want linked devices to tell us what it should be, instead of telling them to
|
||||
// erase their local value.
|
||||
if (!viaStorageServiceSync && profileKey) {
|
||||
this.captureChange('profileKey');
|
||||
}
|
||||
|
||||
this.deriveAccessKeyIfNeeded();
|
||||
|
||||
// We will update the conversation during storage service sync
|
||||
if (!viaStorageServiceSync) {
|
||||
window.Signal.Data.updateConversation(this.attributes);
|
||||
}
|
||||
|
||||
return true;
|
||||
if (oldProfileKey === profileKey) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
log.info(
|
||||
`Setting sealedSender to UNKNOWN for conversation ${this.idForLogging()}`
|
||||
);
|
||||
this.set({
|
||||
profileKeyCredential: null,
|
||||
profileKeyCredentialExpiration: null,
|
||||
accessKey: null,
|
||||
sealedSender: SEALED_SENDER.UNKNOWN,
|
||||
});
|
||||
|
||||
// We messaged the contact when it had either phone number or username
|
||||
// title.
|
||||
if (this.get('needsTitleTransition')) {
|
||||
log.info(
|
||||
`setProfileKey(${this.idForLogging()}): adding a ` +
|
||||
'title transition notification'
|
||||
);
|
||||
|
||||
const { type, e164, username } = this.attributes;
|
||||
|
||||
this.unset('needsTitleTransition');
|
||||
|
||||
await this.addNotification('title-transition-notification', {
|
||||
readStatus: ReadStatus.Read,
|
||||
seenStatus: SeenStatus.Unseen,
|
||||
titleTransition: {
|
||||
renderInfo: {
|
||||
type,
|
||||
e164,
|
||||
username,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// Don't trigger immediate profile fetches when syncing to remote storage
|
||||
this.set({ profileKey }, { silent: viaStorageServiceSync });
|
||||
|
||||
// If our profile key was cleared above, we don't tell our linked devices about it.
|
||||
// We want linked devices to tell us what it should be, instead of telling them to
|
||||
// erase their local value.
|
||||
if (!viaStorageServiceSync && profileKey) {
|
||||
this.captureChange('profileKey');
|
||||
}
|
||||
|
||||
this.deriveAccessKeyIfNeeded();
|
||||
|
||||
// We will update the conversation during storage service sync
|
||||
if (!viaStorageServiceSync) {
|
||||
window.Signal.Data.updateConversation(this.attributes);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
hasProfileKeyCredentialExpired(): boolean {
|
||||
|
|
|
@ -95,6 +95,7 @@ import {
|
|||
isVerifiedChange,
|
||||
isConversationMerge,
|
||||
isPhoneNumberDiscovery,
|
||||
isTitleTransitionNotification,
|
||||
} from '../state/selectors/message';
|
||||
import type { ReactionAttributesType } from '../messageModifiers/Reactions';
|
||||
import { isInCall } from '../state/selectors/calling';
|
||||
|
@ -287,6 +288,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
!isGroupV2Change(attributes) &&
|
||||
!isKeyChange(attributes) &&
|
||||
!isPhoneNumberDiscovery(attributes) &&
|
||||
!isTitleTransitionNotification(attributes) &&
|
||||
!isProfileChange(attributes) &&
|
||||
!isUniversalTimerNotification(attributes) &&
|
||||
!isUnsupportedMessage(attributes) &&
|
||||
|
@ -616,6 +618,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
isUniversalTimerNotification(attributes);
|
||||
const isConversationMergeValue = isConversationMerge(attributes);
|
||||
const isPhoneNumberDiscoveryValue = isPhoneNumberDiscovery(attributes);
|
||||
const isTitleTransitionNotificationValue =
|
||||
isTitleTransitionNotification(attributes);
|
||||
|
||||
const isPayment = messageHasPaymentEvent(attributes);
|
||||
|
||||
|
@ -648,7 +652,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
isProfileChangeValue ||
|
||||
isUniversalTimerNotificationValue ||
|
||||
isConversationMergeValue ||
|
||||
isPhoneNumberDiscoveryValue;
|
||||
isPhoneNumberDiscoveryValue ||
|
||||
isTitleTransitionNotificationValue;
|
||||
|
||||
return !hasSomethingToDisplay;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import type { PropsData as TimerNotificationProps } from '../../components/conve
|
|||
import type { PropsData as ChangeNumberNotificationProps } from '../../components/conversation/ChangeNumberNotification';
|
||||
import type { PropsData as SafetyNumberNotificationProps } from '../../components/conversation/SafetyNumberNotification';
|
||||
import type { PropsData as VerificationNotificationProps } from '../../components/conversation/VerificationNotification';
|
||||
import type { PropsData as TitleTransitionNotificationProps } from '../../components/conversation/TitleTransitionNotification';
|
||||
import type { PropsDataType as GroupsV2Props } from '../../components/conversation/GroupV2Change';
|
||||
import type { PropsDataType as GroupV1MigrationPropsType } from '../../components/conversation/GroupV1Migration';
|
||||
import type { PropsDataType as DeliveryIssuePropsType } from '../../components/conversation/DeliveryIssueNotification';
|
||||
|
@ -129,6 +130,7 @@ import type { AnyPaymentEvent } from '../../types/Payment';
|
|||
import { isPaymentNotificationEvent } from '../../types/Payment';
|
||||
import {
|
||||
getTitleNoDefault,
|
||||
getTitle,
|
||||
getNumber,
|
||||
renderNumber,
|
||||
} from '../../util/getTitle';
|
||||
|
@ -922,6 +924,13 @@ export function getPropsForBubble(
|
|||
timestamp,
|
||||
};
|
||||
}
|
||||
if (isTitleTransitionNotification(message)) {
|
||||
return {
|
||||
type: 'titleTransitionNotification',
|
||||
data: getPropsForTitleTransitionNotification(message),
|
||||
timestamp,
|
||||
};
|
||||
}
|
||||
if (isChatSessionRefreshed(message)) {
|
||||
return {
|
||||
type: 'chatSessionRefreshed',
|
||||
|
@ -1490,6 +1499,31 @@ function getPropsForChangeNumberNotification(
|
|||
};
|
||||
}
|
||||
|
||||
// Title Transition Notification
|
||||
|
||||
export function isTitleTransitionNotification(
|
||||
message: MessageWithUIFieldsType
|
||||
): boolean {
|
||||
return (
|
||||
message.type === 'title-transition-notification' &&
|
||||
message.titleTransition != null
|
||||
);
|
||||
}
|
||||
|
||||
function getPropsForTitleTransitionNotification(
|
||||
message: MessageWithUIFieldsType
|
||||
): TitleTransitionNotificationProps {
|
||||
strictAssert(
|
||||
message.titleTransition != null,
|
||||
'Invalid attributes for title-transition-notification'
|
||||
);
|
||||
const { renderInfo } = message.titleTransition;
|
||||
const oldTitle = getTitle(renderInfo);
|
||||
return {
|
||||
oldTitle,
|
||||
};
|
||||
}
|
||||
|
||||
// Chat Session Refreshed
|
||||
|
||||
export function isChatSessionRefreshed(
|
||||
|
|
|
@ -58,18 +58,12 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
whitelisted: true,
|
||||
serviceE164: pniContact.device.number,
|
||||
identityKey: pniContact.getPublicKey(ServiceIdKind.PNI).serialize(),
|
||||
givenName: 'PNI Contact',
|
||||
givenName: undefined,
|
||||
familyName: undefined,
|
||||
},
|
||||
ServiceIdKind.PNI
|
||||
);
|
||||
|
||||
state = state.addContact(pniContact, {
|
||||
whitelisted: true,
|
||||
serviceE164: undefined,
|
||||
identityKey: pniContact.publicKey.serialize(),
|
||||
profileKey: pniContact.profileKey.serialize(),
|
||||
});
|
||||
|
||||
// Just to make PNI Contact visible in the left pane
|
||||
state = state.pin(pniContact, ServiceIdKind.PNI);
|
||||
|
||||
|
@ -319,6 +313,7 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
await pniContact.sendText(desktop, 'Hello Desktop!', {
|
||||
timestamp: bootstrap.getTimestamp(),
|
||||
withPniSignature: true,
|
||||
withProfileKey: true,
|
||||
});
|
||||
|
||||
debug('Wait for merge to happen');
|
||||
|
@ -377,15 +372,12 @@ describe('pnp/PNI Signature', function (this: Mocha.Suite) {
|
|||
const messages = window.locator('.module-message__text');
|
||||
assert.strictEqual(await messages.count(), 3, 'messages');
|
||||
|
||||
// Merge notification
|
||||
// Title transition notification
|
||||
const notifications = window.locator('.SystemMessage');
|
||||
assert.strictEqual(await notifications.count(), 1, 'notifications');
|
||||
|
||||
const first = await notifications.first();
|
||||
assert.match(
|
||||
await first.innerText(),
|
||||
/Your message history with ACI Contact and their number .* has been merged./
|
||||
);
|
||||
assert.match(await first.innerText(), /You started this chat with/);
|
||||
|
||||
assert.isEmpty(await phone.getOrphanedStorageKeys());
|
||||
}
|
||||
|
|
|
@ -93,7 +93,15 @@ describe('pnp/username', function (this: Mocha.Suite) {
|
|||
.locator(
|
||||
`[data-testid="${usernameContact.device.aci}"] >> "${USERNAME}"`
|
||||
)
|
||||
.waitFor();
|
||||
.click();
|
||||
|
||||
debug('Send message to username');
|
||||
{
|
||||
const compositionInput = await app.waitForEnabledComposer();
|
||||
|
||||
await compositionInput.type('Hello username');
|
||||
await compositionInput.press('Enter');
|
||||
}
|
||||
|
||||
let state = await phone.expectStorageState('consistency check');
|
||||
|
||||
|
@ -141,6 +149,31 @@ describe('pnp/username', function (this: Mocha.Suite) {
|
|||
assert.strictEqual(removed[0].contact?.aci, usernameContact.device.aci);
|
||||
assert.strictEqual(removed[0].contact?.username, USERNAME);
|
||||
}
|
||||
|
||||
if (type === 'system') {
|
||||
// No notifications
|
||||
const notifications = window.locator('.SystemMessage');
|
||||
assert.strictEqual(
|
||||
await notifications.count(),
|
||||
0,
|
||||
'notification count'
|
||||
);
|
||||
} else {
|
||||
// One notification - the username transition
|
||||
const notifications = window.locator('.SystemMessage');
|
||||
await notifications.waitFor();
|
||||
assert.strictEqual(
|
||||
await notifications.count(),
|
||||
1,
|
||||
'notification count'
|
||||
);
|
||||
|
||||
const first = await notifications.first();
|
||||
assert.strictEqual(
|
||||
await first.innerText(),
|
||||
`You started this chat with ${USERNAME}`
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -143,3 +143,30 @@ export function renderNumber(e164: string): string | undefined {
|
|||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasNumberTitle(
|
||||
attributes: Pick<
|
||||
ConversationAttributesType,
|
||||
'e164' | 'type' | 'sharingPhoneNumber' | 'profileKey'
|
||||
>
|
||||
): boolean {
|
||||
return (
|
||||
!getSystemName(attributes) &&
|
||||
!getProfileName(attributes) &&
|
||||
Boolean(getNumber(attributes))
|
||||
);
|
||||
}
|
||||
|
||||
export function hasUsernameTitle(
|
||||
attributes: Pick<
|
||||
ConversationAttributesType,
|
||||
'e164' | 'type' | 'sharingPhoneNumber' | 'profileKey' | 'username'
|
||||
>
|
||||
): boolean {
|
||||
return (
|
||||
!getSystemName(attributes) &&
|
||||
!getProfileName(attributes) &&
|
||||
!getNumber(attributes) &&
|
||||
Boolean(attributes.username)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue