Skip to content

Commit

Permalink
Change Phone Number notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny-signal committed Aug 5, 2021
1 parent 4b82ac3 commit a001882
Show file tree
Hide file tree
Showing 17 changed files with 277 additions and 39 deletions.
10 changes: 10 additions & 0 deletions _locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,16 @@
}
}
},
"ChangeNumber--notification": {
"message": "$sender$ changed their number to a new number",
"description": "Shown in timeline when a member of a conversation changes their phone number",
"placeholders": {
"sender": {
"content": "$1",
"example": "Sam"
}
}
},
"quoteThumbnailAlt": {
"message": "Thumbnail of image from quoted message",
"description": "Used in alt tag of thumbnail images inside of an embedded message quote"
Expand Down
1 change: 1 addition & 0 deletions protos/SignalStorage.proto
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,5 @@ message AccountRecord {
repeated PinnedConversation pinnedConversations = 14;
optional uint32 universalExpireTimer = 17;
optional bool primarySendsSms = 18;
optional string e164 = 19;
}
39 changes: 39 additions & 0 deletions stylesheets/_modules.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2425,6 +2425,45 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
}
}

// Module: Change Number Notification

.module-change-number-notification {
@include font-body-2;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;

@include light-theme {
color: $color-gray-60;
}
@include dark-theme {
color: $color-gray-05;
}

&__icon {
height: 16px;
width: 16px;
display: inline-block;
margin-right: 8px;

@include light-theme {
@include color-svg(
'../images/icons/v2/phone-right-outline-24.svg',
$color-gray-75
);
}
@include dark-theme {
@include color-svg(
'../images/icons/v2/phone-right-solid-24.svg',
$color-gray-15
);
}
}
}

// Module: Error Boundary

.module-error-boundary-notification {
text-align: center;
cursor: pointer;
Expand Down
43 changes: 30 additions & 13 deletions ts/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
ReadSyncEvent,
ContactEvent,
GroupEvent,
EnvelopeEvent,
} from './textsecure/messageReceiverEvents';
import type { WebAPIType } from './textsecure/WebAPI';
import * as universalExpireTimer from './util/universalExpireTimer';
Expand Down Expand Up @@ -174,6 +175,10 @@ export async function startApp(): Promise<void> {
};
}

messageReceiver.addEventListener(
'envelope',
queuedEventListener(onEnvelopeReceived, false)
);
messageReceiver.addEventListener(
'message',
queuedEventListener(onMessageReceived, false)
Expand Down Expand Up @@ -503,17 +508,7 @@ export async function startApp(): Promise<void> {

accountManager = new window.textsecure.AccountManager(server);
accountManager.addEventListener('registration', () => {
const ourDeviceId = window.textsecure.storage.user.getDeviceId();
const ourNumber = window.textsecure.storage.user.getNumber();
const ourUuid = window.textsecure.storage.user.getUuid();
const user = {
ourConversationId: window.ConversationController.getOurConversationId(),
ourDeviceId,
ourNumber,
ourUuid,
regionCode: window.storage.get('regionCode'),
};
window.Whisper.events.trigger('userChanged', user);
window.Whisper.events.trigger('userChanged');

window.Signal.Util.Registration.markDone();
window.log.info('dispatching registration event');
Expand Down Expand Up @@ -1210,7 +1205,6 @@ export async function startApp(): Promise<void> {
conversationRemoved,
removeAllConversations,
} = window.reduxActions.conversations;
const { userChanged } = window.reduxActions.user;

convoCollection.on('remove', conversation => {
const { id } = conversation || {};
Expand Down Expand Up @@ -1254,7 +1248,19 @@ export async function startApp(): Promise<void> {
});
convoCollection.on('reset', removeAllConversations);

window.Whisper.events.on('userChanged', userChanged);
window.Whisper.events.on('userChanged', () => {
const newDeviceId = window.textsecure.storage.user.getDeviceId();
const newNumber = window.textsecure.storage.user.getNumber();
const newUuid = window.textsecure.storage.user.getUuid();

window.reduxActions.user.userChanged({
ourConversationId: window.ConversationController.getOurConversationId(),
ourDeviceId: newDeviceId,
ourNumber: newNumber,
ourUuid: newUuid,
regionCode: window.storage.get('regionCode'),
});
});

let shortcutGuideView: WhatIsThis | null = null;

Expand Down Expand Up @@ -3000,6 +3006,17 @@ export async function startApp(): Promise<void> {
maxSize: Infinity,
});

function onEnvelopeReceived({ envelope }: EnvelopeEvent) {
const ourUuid = window.textsecure.storage.user.getUuid();
if (envelope.sourceUuid && envelope.sourceUuid !== ourUuid) {
window.ConversationController.ensureContactIds({
e164: envelope.source,
uuid: envelope.sourceUuid,
highTrust: true,
});
}
}

// Note: We do very little in this function, since everything in handleDataMessage is
// inside a conversation-specific queue(). Any code here might run before an earlier
// message is processed in handleDataMessage().
Expand Down
43 changes: 43 additions & 0 deletions ts/components/conversation/ChangeNumberNotification.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import React from 'react';

import { ConversationType } from '../../state/ducks/conversations';
import { LocalizerType } from '../../types/Util';
import { Intl } from '../Intl';

import { Timestamp } from './Timestamp';
import { Emojify } from './Emojify';

export type PropsData = {
sender: ConversationType;
timestamp: number;
};

export type PropsHousekeeping = {
i18n: LocalizerType;
};

export type Props = PropsData & PropsHousekeeping;

const CSS_MODULE = 'module-change-number-notification';

export const ChangeNumberNotification: React.FC<Props> = props => {
const { i18n, sender, timestamp } = props;

return (
<div className={CSS_MODULE}>
<span className={`${CSS_MODULE}__icon`} />
<Intl
id="ChangeNumber--notification"
components={{
sender: <Emojify text={sender.firstName || sender.title} />,
}}
i18n={i18n}
/>
&nbsp;·&nbsp;
<Timestamp i18n={i18n} timestamp={timestamp} />
</div>
);
};
7 changes: 7 additions & 0 deletions ts/components/conversation/TimelineItem.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ storiesOf('Components/Conversation/TimelineItem', module)
type: 'universalTimerNotification',
data: null,
},
{
type: 'changeNumberNotification',
data: {
sender: getDefaultConversation(),
timestamp: Date.now(),
},
},
{
type: 'callHistory',
data: {
Expand Down
13 changes: 13 additions & 0 deletions ts/components/conversation/TimelineItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ import {
PropsActionsType as DeliveryIssueActionProps,
PropsDataType as DeliveryIssueProps,
} from './DeliveryIssueNotification';
import {
ChangeNumberNotification,
PropsData as ChangeNumberNotificationProps,
} from './ChangeNumberNotification';
import { CallingNotificationType } from '../../util/callingNotification';
import { InlineNotificationWrapper } from './InlineNotificationWrapper';
import {
Expand Down Expand Up @@ -95,6 +99,10 @@ type UniversalTimerNotificationType = {
type: 'universalTimerNotification';
data: null;
};
type ChangeNumberNotificationType = {
type: 'changeNumberNotification';
data: ChangeNumberNotificationProps;
};
type SafetyNumberNotificationType = {
type: 'safetyNumberNotification';
data: SafetyNumberNotificationProps;
Expand Down Expand Up @@ -138,6 +146,7 @@ export type TimelineItemType =
| SafetyNumberNotificationType
| TimerNotificationType
| UniversalTimerNotificationType
| ChangeNumberNotificationType
| UnsupportedMessageType
| VerificationNotificationType;

Expand Down Expand Up @@ -244,6 +253,10 @@ export class TimelineItem extends React.PureComponent<PropsType> {
);
} else if (item.type === 'universalTimerNotification') {
notification = renderUniversalTimerNotification();
} else if (item.type === 'changeNumberNotification') {
notification = (
<ChangeNumberNotification {...this.props} {...item.data} i18n={i18n} />
);
} else if (item.type === 'safetyNumberNotification') {
notification = (
<SafetyNumberNotification {...this.props} {...item.data} i18n={i18n} />
Expand Down
1 change: 1 addition & 0 deletions ts/model-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export type MessageAttributesType = {
| 'profile-change'
| 'timer-notification'
| 'universal-timer-notification'
| 'change-number-notification'
| 'verified-change';
body?: string;
attachments?: Array<AttachmentType>;
Expand Down
58 changes: 45 additions & 13 deletions ts/models/conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ export class ConversationModel extends window.Backbone

jobQueue?: typeof window.PQueueType;

ourNumber?: string;

ourUuid?: string;

storeName?: string | null;
Expand Down Expand Up @@ -234,7 +232,6 @@ export class ConversationModel extends window.Backbone

this.storeName = 'conversations';

this.ourNumber = window.textsecure.storage.user.getNumber();
this.ourUuid = window.textsecure.storage.user.getUuid();
this.verifiedEnum = window.textsecure.storage.protocol.VerifiedStatus;

Expand Down Expand Up @@ -1497,6 +1494,11 @@ export class ConversationModel extends window.Backbone
const oldValue = this.get('e164');
if (e164 && e164 !== oldValue) {
this.set('e164', e164);

if (oldValue) {
this.addChangeNumberNotification();
}

window.Signal.Data.updateConversation(this.attributes);
this.trigger('idUpdated', this, 'e164', oldValue);
}
Expand Down Expand Up @@ -2688,7 +2690,7 @@ export class ConversationModel extends window.Backbone
sent_at: now,
received_at: window.Signal.Util.incrementMessageCounter(),
received_at_ms: now,
unread: 0,
unread: false,
changedId: conversationId || this.id,
profileChange,
// TODO: DESKTOP-722
Expand Down Expand Up @@ -2716,23 +2718,30 @@ export class ConversationModel extends window.Backbone
}
}

async addUniversalTimerNotification(): Promise<string> {
async addNotification(
type: MessageAttributesType['type'],
extra: Partial<MessageAttributesType> = {}
): Promise<string> {
const now = Date.now();
const message = ({
const message: Partial<MessageAttributesType> = {
...extra,

conversationId: this.id,
type: 'universal-timer-notification',
type,
sent_at: now,
received_at: window.Signal.Util.incrementMessageCounter(),
received_at_ms: now,
unread: 0,
// TODO: DESKTOP-722
} as unknown) as typeof window.Whisper.MessageAttributesType;
unread: false,
};

const id = await window.Signal.Data.saveMessage(message);
const id = await window.Signal.Data.saveMessage(
// TODO: DESKTOP-722
message as MessageAttributesType
);
const model = window.MessageController.register(
id,
new window.Whisper.Message({
...message,
...(message as MessageAttributesType),
id,
})
);
Expand Down Expand Up @@ -2764,7 +2773,9 @@ export class ConversationModel extends window.Backbone
return;
}

const notificationId = await this.addUniversalTimerNotification();
const notificationId = await this.addNotification(
'universal-timer-notification'
);
this.set('pendingUniversalTimer', notificationId);
}

Expand Down Expand Up @@ -2797,6 +2808,27 @@ export class ConversationModel extends window.Backbone
this.set('pendingUniversalTimer', undefined);
}

async addChangeNumberNotification(): Promise<void> {
window.log.info(
`Conversation ${this.idForLogging()}: adding change number notification`
);

const convos = [
this,
...(await window.ConversationController.getAllGroupsInvolvingId(this.id)),
];

const sourceUuid = this.get('uuid');

await Promise.all(
convos.map(convo => {
return convo.addNotification('change-number-notification', {
sourceUuid,
});
})
);
}

async onReadMessage(
message: MessageModel,
readAt?: number
Expand Down

0 comments on commit a001882

Please sign in to comment.