Change Phone Number notifications
This commit is contained in:
parent
4b82ac387b
commit
a001882d58
17 changed files with 277 additions and 39 deletions
|
@ -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": {
|
"quoteThumbnailAlt": {
|
||||||
"message": "Thumbnail of image from quoted message",
|
"message": "Thumbnail of image from quoted message",
|
||||||
"description": "Used in alt tag of thumbnail images inside of an embedded message quote"
|
"description": "Used in alt tag of thumbnail images inside of an embedded message quote"
|
||||||
|
|
|
@ -135,4 +135,5 @@ message AccountRecord {
|
||||||
repeated PinnedConversation pinnedConversations = 14;
|
repeated PinnedConversation pinnedConversations = 14;
|
||||||
optional uint32 universalExpireTimer = 17;
|
optional uint32 universalExpireTimer = 17;
|
||||||
optional bool primarySendsSms = 18;
|
optional bool primarySendsSms = 18;
|
||||||
|
optional string e164 = 19;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
.module-error-boundary-notification {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
|
@ -60,6 +60,7 @@ import {
|
||||||
ReadSyncEvent,
|
ReadSyncEvent,
|
||||||
ContactEvent,
|
ContactEvent,
|
||||||
GroupEvent,
|
GroupEvent,
|
||||||
|
EnvelopeEvent,
|
||||||
} from './textsecure/messageReceiverEvents';
|
} from './textsecure/messageReceiverEvents';
|
||||||
import type { WebAPIType } from './textsecure/WebAPI';
|
import type { WebAPIType } from './textsecure/WebAPI';
|
||||||
import * as universalExpireTimer from './util/universalExpireTimer';
|
import * as universalExpireTimer from './util/universalExpireTimer';
|
||||||
|
@ -174,6 +175,10 @@ export async function startApp(): Promise<void> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messageReceiver.addEventListener(
|
||||||
|
'envelope',
|
||||||
|
queuedEventListener(onEnvelopeReceived, false)
|
||||||
|
);
|
||||||
messageReceiver.addEventListener(
|
messageReceiver.addEventListener(
|
||||||
'message',
|
'message',
|
||||||
queuedEventListener(onMessageReceived, false)
|
queuedEventListener(onMessageReceived, false)
|
||||||
|
@ -503,17 +508,7 @@ export async function startApp(): Promise<void> {
|
||||||
|
|
||||||
accountManager = new window.textsecure.AccountManager(server);
|
accountManager = new window.textsecure.AccountManager(server);
|
||||||
accountManager.addEventListener('registration', () => {
|
accountManager.addEventListener('registration', () => {
|
||||||
const ourDeviceId = window.textsecure.storage.user.getDeviceId();
|
window.Whisper.events.trigger('userChanged');
|
||||||
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.Signal.Util.Registration.markDone();
|
window.Signal.Util.Registration.markDone();
|
||||||
window.log.info('dispatching registration event');
|
window.log.info('dispatching registration event');
|
||||||
|
@ -1210,7 +1205,6 @@ export async function startApp(): Promise<void> {
|
||||||
conversationRemoved,
|
conversationRemoved,
|
||||||
removeAllConversations,
|
removeAllConversations,
|
||||||
} = window.reduxActions.conversations;
|
} = window.reduxActions.conversations;
|
||||||
const { userChanged } = window.reduxActions.user;
|
|
||||||
|
|
||||||
convoCollection.on('remove', conversation => {
|
convoCollection.on('remove', conversation => {
|
||||||
const { id } = conversation || {};
|
const { id } = conversation || {};
|
||||||
|
@ -1254,7 +1248,19 @@ export async function startApp(): Promise<void> {
|
||||||
});
|
});
|
||||||
convoCollection.on('reset', removeAllConversations);
|
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;
|
let shortcutGuideView: WhatIsThis | null = null;
|
||||||
|
|
||||||
|
@ -3000,6 +3006,17 @@ export async function startApp(): Promise<void> {
|
||||||
maxSize: Infinity,
|
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
|
// 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
|
// inside a conversation-specific queue(). Any code here might run before an earlier
|
||||||
// message is processed in handleDataMessage().
|
// message is processed in handleDataMessage().
|
||||||
|
|
43
ts/components/conversation/ChangeNumberNotification.tsx
Normal file
43
ts/components/conversation/ChangeNumberNotification.tsx
Normal file
|
@ -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}
|
||||||
|
/>
|
||||||
|
·
|
||||||
|
<Timestamp i18n={i18n} timestamp={timestamp} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -129,6 +129,13 @@ storiesOf('Components/Conversation/TimelineItem', module)
|
||||||
type: 'universalTimerNotification',
|
type: 'universalTimerNotification',
|
||||||
data: null,
|
data: null,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'changeNumberNotification',
|
||||||
|
data: {
|
||||||
|
sender: getDefaultConversation(),
|
||||||
|
timestamp: Date.now(),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'callHistory',
|
type: 'callHistory',
|
||||||
data: {
|
data: {
|
||||||
|
|
|
@ -24,6 +24,10 @@ import {
|
||||||
PropsActionsType as DeliveryIssueActionProps,
|
PropsActionsType as DeliveryIssueActionProps,
|
||||||
PropsDataType as DeliveryIssueProps,
|
PropsDataType as DeliveryIssueProps,
|
||||||
} from './DeliveryIssueNotification';
|
} from './DeliveryIssueNotification';
|
||||||
|
import {
|
||||||
|
ChangeNumberNotification,
|
||||||
|
PropsData as ChangeNumberNotificationProps,
|
||||||
|
} from './ChangeNumberNotification';
|
||||||
import { CallingNotificationType } from '../../util/callingNotification';
|
import { CallingNotificationType } from '../../util/callingNotification';
|
||||||
import { InlineNotificationWrapper } from './InlineNotificationWrapper';
|
import { InlineNotificationWrapper } from './InlineNotificationWrapper';
|
||||||
import {
|
import {
|
||||||
|
@ -95,6 +99,10 @@ type UniversalTimerNotificationType = {
|
||||||
type: 'universalTimerNotification';
|
type: 'universalTimerNotification';
|
||||||
data: null;
|
data: null;
|
||||||
};
|
};
|
||||||
|
type ChangeNumberNotificationType = {
|
||||||
|
type: 'changeNumberNotification';
|
||||||
|
data: ChangeNumberNotificationProps;
|
||||||
|
};
|
||||||
type SafetyNumberNotificationType = {
|
type SafetyNumberNotificationType = {
|
||||||
type: 'safetyNumberNotification';
|
type: 'safetyNumberNotification';
|
||||||
data: SafetyNumberNotificationProps;
|
data: SafetyNumberNotificationProps;
|
||||||
|
@ -138,6 +146,7 @@ export type TimelineItemType =
|
||||||
| SafetyNumberNotificationType
|
| SafetyNumberNotificationType
|
||||||
| TimerNotificationType
|
| TimerNotificationType
|
||||||
| UniversalTimerNotificationType
|
| UniversalTimerNotificationType
|
||||||
|
| ChangeNumberNotificationType
|
||||||
| UnsupportedMessageType
|
| UnsupportedMessageType
|
||||||
| VerificationNotificationType;
|
| VerificationNotificationType;
|
||||||
|
|
||||||
|
@ -244,6 +253,10 @@ export class TimelineItem extends React.PureComponent<PropsType> {
|
||||||
);
|
);
|
||||||
} else if (item.type === 'universalTimerNotification') {
|
} else if (item.type === 'universalTimerNotification') {
|
||||||
notification = renderUniversalTimerNotification();
|
notification = renderUniversalTimerNotification();
|
||||||
|
} else if (item.type === 'changeNumberNotification') {
|
||||||
|
notification = (
|
||||||
|
<ChangeNumberNotification {...this.props} {...item.data} i18n={i18n} />
|
||||||
|
);
|
||||||
} else if (item.type === 'safetyNumberNotification') {
|
} else if (item.type === 'safetyNumberNotification') {
|
||||||
notification = (
|
notification = (
|
||||||
<SafetyNumberNotification {...this.props} {...item.data} i18n={i18n} />
|
<SafetyNumberNotification {...this.props} {...item.data} i18n={i18n} />
|
||||||
|
|
1
ts/model-types.d.ts
vendored
1
ts/model-types.d.ts
vendored
|
@ -141,6 +141,7 @@ export type MessageAttributesType = {
|
||||||
| 'profile-change'
|
| 'profile-change'
|
||||||
| 'timer-notification'
|
| 'timer-notification'
|
||||||
| 'universal-timer-notification'
|
| 'universal-timer-notification'
|
||||||
|
| 'change-number-notification'
|
||||||
| 'verified-change';
|
| 'verified-change';
|
||||||
body?: string;
|
body?: string;
|
||||||
attachments?: Array<AttachmentType>;
|
attachments?: Array<AttachmentType>;
|
||||||
|
|
|
@ -152,8 +152,6 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
jobQueue?: typeof window.PQueueType;
|
jobQueue?: typeof window.PQueueType;
|
||||||
|
|
||||||
ourNumber?: string;
|
|
||||||
|
|
||||||
ourUuid?: string;
|
ourUuid?: string;
|
||||||
|
|
||||||
storeName?: string | null;
|
storeName?: string | null;
|
||||||
|
@ -234,7 +232,6 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
this.storeName = 'conversations';
|
this.storeName = 'conversations';
|
||||||
|
|
||||||
this.ourNumber = window.textsecure.storage.user.getNumber();
|
|
||||||
this.ourUuid = window.textsecure.storage.user.getUuid();
|
this.ourUuid = window.textsecure.storage.user.getUuid();
|
||||||
this.verifiedEnum = window.textsecure.storage.protocol.VerifiedStatus;
|
this.verifiedEnum = window.textsecure.storage.protocol.VerifiedStatus;
|
||||||
|
|
||||||
|
@ -1497,6 +1494,11 @@ export class ConversationModel extends window.Backbone
|
||||||
const oldValue = this.get('e164');
|
const oldValue = this.get('e164');
|
||||||
if (e164 && e164 !== oldValue) {
|
if (e164 && e164 !== oldValue) {
|
||||||
this.set('e164', e164);
|
this.set('e164', e164);
|
||||||
|
|
||||||
|
if (oldValue) {
|
||||||
|
this.addChangeNumberNotification();
|
||||||
|
}
|
||||||
|
|
||||||
window.Signal.Data.updateConversation(this.attributes);
|
window.Signal.Data.updateConversation(this.attributes);
|
||||||
this.trigger('idUpdated', this, 'e164', oldValue);
|
this.trigger('idUpdated', this, 'e164', oldValue);
|
||||||
}
|
}
|
||||||
|
@ -2688,7 +2690,7 @@ export class ConversationModel extends window.Backbone
|
||||||
sent_at: now,
|
sent_at: now,
|
||||||
received_at: window.Signal.Util.incrementMessageCounter(),
|
received_at: window.Signal.Util.incrementMessageCounter(),
|
||||||
received_at_ms: now,
|
received_at_ms: now,
|
||||||
unread: 0,
|
unread: false,
|
||||||
changedId: conversationId || this.id,
|
changedId: conversationId || this.id,
|
||||||
profileChange,
|
profileChange,
|
||||||
// TODO: DESKTOP-722
|
// TODO: DESKTOP-722
|
||||||
|
@ -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 now = Date.now();
|
||||||
const message = ({
|
const message: Partial<MessageAttributesType> = {
|
||||||
|
...extra,
|
||||||
|
|
||||||
conversationId: this.id,
|
conversationId: this.id,
|
||||||
type: 'universal-timer-notification',
|
type,
|
||||||
sent_at: now,
|
sent_at: now,
|
||||||
received_at: window.Signal.Util.incrementMessageCounter(),
|
received_at: window.Signal.Util.incrementMessageCounter(),
|
||||||
received_at_ms: now,
|
received_at_ms: now,
|
||||||
unread: 0,
|
unread: false,
|
||||||
// TODO: DESKTOP-722
|
};
|
||||||
} as unknown) as typeof window.Whisper.MessageAttributesType;
|
|
||||||
|
|
||||||
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(
|
const model = window.MessageController.register(
|
||||||
id,
|
id,
|
||||||
new window.Whisper.Message({
|
new window.Whisper.Message({
|
||||||
...message,
|
...(message as MessageAttributesType),
|
||||||
id,
|
id,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -2764,7 +2773,9 @@ export class ConversationModel extends window.Backbone
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const notificationId = await this.addUniversalTimerNotification();
|
const notificationId = await this.addNotification(
|
||||||
|
'universal-timer-notification'
|
||||||
|
);
|
||||||
this.set('pendingUniversalTimer', notificationId);
|
this.set('pendingUniversalTimer', notificationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2797,6 +2808,27 @@ export class ConversationModel extends window.Backbone
|
||||||
this.set('pendingUniversalTimer', undefined);
|
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(
|
async onReadMessage(
|
||||||
message: MessageModel,
|
message: MessageModel,
|
||||||
readAt?: number
|
readAt?: number
|
||||||
|
|
|
@ -178,8 +178,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
|
|
||||||
INITIAL_PROTOCOL_VERSION?: number;
|
INITIAL_PROTOCOL_VERSION?: number;
|
||||||
|
|
||||||
OUR_NUMBER?: string;
|
|
||||||
|
|
||||||
OUR_UUID?: string;
|
OUR_UUID?: string;
|
||||||
|
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
|
@ -209,7 +207,6 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
|
|
||||||
this.CURRENT_PROTOCOL_VERSION = Proto.DataMessage.ProtocolVersion.CURRENT;
|
this.CURRENT_PROTOCOL_VERSION = Proto.DataMessage.ProtocolVersion.CURRENT;
|
||||||
this.INITIAL_PROTOCOL_VERSION = Proto.DataMessage.ProtocolVersion.INITIAL;
|
this.INITIAL_PROTOCOL_VERSION = Proto.DataMessage.ProtocolVersion.INITIAL;
|
||||||
this.OUR_NUMBER = window.textsecure.storage.user.getNumber();
|
|
||||||
this.OUR_UUID = window.textsecure.storage.user.getUuid();
|
this.OUR_UUID = window.textsecure.storage.user.getUuid();
|
||||||
|
|
||||||
this.on('change', this.notifyRedux);
|
this.on('change', this.notifyRedux);
|
||||||
|
@ -364,7 +361,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
this.attributes,
|
this.attributes,
|
||||||
findAndFormatContact,
|
findAndFormatContact,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
this.OUR_NUMBER,
|
window.textsecure.storage.user.getNumber(),
|
||||||
this.OUR_UUID,
|
this.OUR_UUID,
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
|
@ -1066,7 +1063,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.OUR_NUMBER;
|
return window.textsecure.storage.user.getNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceDevice(): string | number | undefined {
|
getSourceDevice(): string | number | undefined {
|
||||||
|
@ -1280,11 +1277,12 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
const quoteWithData = await loadQuoteData(this.get('quote'));
|
const quoteWithData = await loadQuoteData(this.get('quote'));
|
||||||
const previewWithData = await loadPreviewData(this.get('preview'));
|
const previewWithData = await loadPreviewData(this.get('preview'));
|
||||||
const stickerWithData = await loadStickerData(this.get('sticker'));
|
const stickerWithData = await loadStickerData(this.get('sticker'));
|
||||||
|
const ourNumber = window.textsecure.storage.user.getNumber();
|
||||||
|
|
||||||
// Special-case the self-send case - we send only a sync message
|
// Special-case the self-send case - we send only a sync message
|
||||||
if (
|
if (
|
||||||
recipients.length === 1 &&
|
recipients.length === 1 &&
|
||||||
(recipients[0] === this.OUR_NUMBER || recipients[0] === this.OUR_UUID)
|
(recipients[0] === ourNumber || recipients[0] === this.OUR_UUID)
|
||||||
) {
|
) {
|
||||||
const dataMessage = await window.textsecure.messaging.getDataMessage({
|
const dataMessage = await window.textsecure.messaging.getDataMessage({
|
||||||
attachments,
|
attachments,
|
||||||
|
@ -1434,9 +1432,10 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
const quoteWithData = await loadQuoteData(this.get('quote'));
|
const quoteWithData = await loadQuoteData(this.get('quote'));
|
||||||
const previewWithData = await loadPreviewData(this.get('preview'));
|
const previewWithData = await loadPreviewData(this.get('preview'));
|
||||||
const stickerWithData = await loadStickerData(this.get('sticker'));
|
const stickerWithData = await loadStickerData(this.get('sticker'));
|
||||||
|
const ourNumber = window.textsecure.storage.user.getNumber();
|
||||||
|
|
||||||
// Special-case the self-send case - we send only a sync message
|
// Special-case the self-send case - we send only a sync message
|
||||||
if (identifier === this.OUR_NUMBER || identifier === this.OUR_UUID) {
|
if (identifier === ourNumber || identifier === this.OUR_UUID) {
|
||||||
const dataMessage = await window.textsecure.messaging.getDataMessage({
|
const dataMessage = await window.textsecure.messaging.getDataMessage({
|
||||||
attachments,
|
attachments,
|
||||||
body,
|
body,
|
||||||
|
|
|
@ -186,6 +186,11 @@ export async function toAccountRecord(
|
||||||
accountRecord.primarySendsSms = Boolean(primarySendsSms);
|
accountRecord.primarySendsSms = Boolean(primarySendsSms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const accountE164 = window.storage.get('accountE164');
|
||||||
|
if (accountE164 !== undefined) {
|
||||||
|
accountRecord.e164 = accountE164;
|
||||||
|
}
|
||||||
|
|
||||||
const universalExpireTimer = getUniversalExpireTimer();
|
const universalExpireTimer = getUniversalExpireTimer();
|
||||||
if (universalExpireTimer) {
|
if (universalExpireTimer) {
|
||||||
accountRecord.universalExpireTimer = Number(universalExpireTimer);
|
accountRecord.universalExpireTimer = Number(universalExpireTimer);
|
||||||
|
@ -828,6 +833,7 @@ export async function mergeAccountRecord(
|
||||||
typingIndicators,
|
typingIndicators,
|
||||||
primarySendsSms,
|
primarySendsSms,
|
||||||
universalExpireTimer,
|
universalExpireTimer,
|
||||||
|
e164: accountE164,
|
||||||
} = accountRecord;
|
} = accountRecord;
|
||||||
|
|
||||||
window.storage.put('read-receipt-setting', Boolean(readReceipts));
|
window.storage.put('read-receipt-setting', Boolean(readReceipts));
|
||||||
|
@ -848,6 +854,11 @@ export async function mergeAccountRecord(
|
||||||
window.storage.put('primarySendsSms', primarySendsSms);
|
window.storage.put('primarySendsSms', primarySendsSms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof accountE164 === 'string') {
|
||||||
|
window.storage.put('accountE164', accountE164);
|
||||||
|
window.storage.user.setNumber(accountE164);
|
||||||
|
}
|
||||||
|
|
||||||
setUniversalExpireTimer(universalExpireTimer || 0);
|
setUniversalExpireTimer(universalExpireTimer || 0);
|
||||||
|
|
||||||
const PHONE_NUMBER_SHARING_MODE_ENUM =
|
const PHONE_NUMBER_SHARING_MODE_ENUM =
|
||||||
|
|
|
@ -3522,7 +3522,8 @@ async function hasUserInitiatedMessages(
|
||||||
'message-history-unsynced',
|
'message-history-unsynced',
|
||||||
'keychange',
|
'keychange',
|
||||||
'group-v1-migration',
|
'group-v1-migration',
|
||||||
'universal-timer-notification'
|
'universal-timer-notification',
|
||||||
|
'change-number-notification'
|
||||||
)
|
)
|
||||||
) AND
|
) AND
|
||||||
json_extract(json, '$.expirationTimerUpdate') IS NULL
|
json_extract(json, '$.expirationTimerUpdate') IS NULL
|
||||||
|
@ -4226,7 +4227,8 @@ async function getLastConversationActivity({
|
||||||
'message-history-unsynced',
|
'message-history-unsynced',
|
||||||
'keychange',
|
'keychange',
|
||||||
'group-v1-migration',
|
'group-v1-migration',
|
||||||
'universal-timer-notification'
|
'universal-timer-notification',
|
||||||
|
'change-number-notification'
|
||||||
)
|
)
|
||||||
) AND
|
) AND
|
||||||
(
|
(
|
||||||
|
@ -4277,7 +4279,8 @@ async function getLastConversationPreview({
|
||||||
'verified-change',
|
'verified-change',
|
||||||
'message-history-unsynced',
|
'message-history-unsynced',
|
||||||
'group-v1-migration',
|
'group-v1-migration',
|
||||||
'universal-timer-notification'
|
'universal-timer-notification',
|
||||||
|
'change-number-notification'
|
||||||
)
|
)
|
||||||
) AND NOT
|
) AND NOT
|
||||||
(
|
(
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
import { TimelineItemType } from '../../components/conversation/TimelineItem';
|
import { TimelineItemType } from '../../components/conversation/TimelineItem';
|
||||||
import { PropsData } from '../../components/conversation/Message';
|
import { PropsData } from '../../components/conversation/Message';
|
||||||
import { PropsData as TimerNotificationProps } from '../../components/conversation/TimerNotification';
|
import { PropsData as TimerNotificationProps } from '../../components/conversation/TimerNotification';
|
||||||
|
import { PropsData as ChangeNumberNotificationProps } from '../../components/conversation/ChangeNumberNotification';
|
||||||
import { PropsData as SafetyNumberNotificationProps } from '../../components/conversation/SafetyNumberNotification';
|
import { PropsData as SafetyNumberNotificationProps } from '../../components/conversation/SafetyNumberNotification';
|
||||||
import { PropsData as VerificationNotificationProps } from '../../components/conversation/VerificationNotification';
|
import { PropsData as VerificationNotificationProps } from '../../components/conversation/VerificationNotification';
|
||||||
import { PropsDataType as GroupsV2Props } from '../../components/conversation/GroupV2Change';
|
import { PropsDataType as GroupsV2Props } from '../../components/conversation/GroupV2Change';
|
||||||
|
@ -188,6 +189,12 @@ export function getPropsForBubble(
|
||||||
data: null,
|
data: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (isChangeNumberNotification(message)) {
|
||||||
|
return {
|
||||||
|
type: 'changeNumberNotification',
|
||||||
|
data: getPropsForChangeNumberNotification(message, conversationSelector),
|
||||||
|
};
|
||||||
|
}
|
||||||
if (isChatSessionRefreshed(message)) {
|
if (isChatSessionRefreshed(message)) {
|
||||||
return {
|
return {
|
||||||
type: 'chatSessionRefreshed',
|
type: 'chatSessionRefreshed',
|
||||||
|
@ -852,6 +859,24 @@ export function isUniversalTimerNotification(
|
||||||
return message.type === 'universal-timer-notification';
|
return message.type === 'universal-timer-notification';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Change Number Notification
|
||||||
|
|
||||||
|
export function isChangeNumberNotification(
|
||||||
|
message: MessageAttributesType
|
||||||
|
): boolean {
|
||||||
|
return message.type === 'change-number-notification';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPropsForChangeNumberNotification(
|
||||||
|
message: MessageAttributesType,
|
||||||
|
conversationSelector: GetConversationByIdType
|
||||||
|
): ChangeNumberNotificationProps {
|
||||||
|
return {
|
||||||
|
sender: conversationSelector(message.sourceUuid),
|
||||||
|
timestamp: message.sent_at,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Chat Session Refreshed
|
// Chat Session Refreshed
|
||||||
|
|
||||||
export function isChatSessionRefreshed(
|
export function isChatSessionRefreshed(
|
||||||
|
|
|
@ -95,6 +95,7 @@ import {
|
||||||
ContactSyncEvent,
|
ContactSyncEvent,
|
||||||
GroupEvent,
|
GroupEvent,
|
||||||
GroupSyncEvent,
|
GroupSyncEvent,
|
||||||
|
EnvelopeEvent,
|
||||||
} from './messageReceiverEvents';
|
} from './messageReceiverEvents';
|
||||||
|
|
||||||
// TODO: remove once we move away from ArrayBuffers
|
// TODO: remove once we move away from ArrayBuffers
|
||||||
|
@ -459,6 +460,11 @@ export default class MessageReceiver
|
||||||
handler: (ev: GroupSyncEvent) => void
|
handler: (ev: GroupSyncEvent) => void
|
||||||
): void;
|
): void;
|
||||||
|
|
||||||
|
public addEventListener(
|
||||||
|
name: 'envelope',
|
||||||
|
handler: (ev: EnvelopeEvent) => void
|
||||||
|
): void;
|
||||||
|
|
||||||
public addEventListener(name: string, handler: EventHandler): void {
|
public addEventListener(name: string, handler: EventHandler): void {
|
||||||
return super.addEventListener(name, handler);
|
return super.addEventListener(name, handler);
|
||||||
}
|
}
|
||||||
|
@ -981,8 +987,6 @@ export default class MessageReceiver
|
||||||
if (this.stoppingProcessing) {
|
if (this.stoppingProcessing) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// No decryption is required for delivery receipts, so the decrypted field of
|
|
||||||
// the Unprocessed model will never be set
|
|
||||||
|
|
||||||
if (envelope.content) {
|
if (envelope.content) {
|
||||||
await this.innerHandleContentMessage(envelope, plaintext);
|
await this.innerHandleContentMessage(envelope, plaintext);
|
||||||
|
|
|
@ -5,7 +5,11 @@
|
||||||
import { PublicKey } from '@signalapp/signal-client';
|
import { PublicKey } from '@signalapp/signal-client';
|
||||||
|
|
||||||
import { SignalService as Proto } from '../protobuf';
|
import { SignalService as Proto } from '../protobuf';
|
||||||
import { ProcessedDataMessage, ProcessedSent } from './Types.d';
|
import {
|
||||||
|
ProcessedEnvelope,
|
||||||
|
ProcessedDataMessage,
|
||||||
|
ProcessedSent,
|
||||||
|
} from './Types.d';
|
||||||
import type {
|
import type {
|
||||||
ModifiedContactDetails,
|
ModifiedContactDetails,
|
||||||
ModifiedGroupDetails,
|
ModifiedGroupDetails,
|
||||||
|
@ -136,6 +140,12 @@ export class GroupSyncEvent extends Event {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class EnvelopeEvent extends Event {
|
||||||
|
constructor(public readonly envelope: ProcessedEnvelope) {
|
||||||
|
super('envelope');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Confirmable events below
|
// Confirmable events below
|
||||||
//
|
//
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import { WebAPICredentials } from '../Types.d';
|
import { WebAPICredentials } from '../Types.d';
|
||||||
|
|
||||||
|
import { strictAssert } from '../../util/assert';
|
||||||
import { StorageInterface } from '../../types/Storage.d';
|
import { StorageInterface } from '../../types/Storage.d';
|
||||||
|
|
||||||
import Helpers from '../Helpers';
|
import Helpers from '../Helpers';
|
||||||
|
@ -27,6 +28,25 @@ export class User {
|
||||||
window.log.info('storage.user: uuid and device id changed');
|
window.log.info('storage.user: uuid and device id changed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async setNumber(number: string): Promise<void> {
|
||||||
|
if (this.getNumber() === number) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deviceId = this.getDeviceId();
|
||||||
|
strictAssert(
|
||||||
|
deviceId !== undefined,
|
||||||
|
'Cannot update device number without knowing device id'
|
||||||
|
);
|
||||||
|
|
||||||
|
window.log.info('storage.user: number changed');
|
||||||
|
|
||||||
|
await this.storage.put('number_id', `${number}.${deviceId}`);
|
||||||
|
|
||||||
|
// Notify redux about phone number change
|
||||||
|
window.Whisper.events.trigger('userChanged');
|
||||||
|
}
|
||||||
|
|
||||||
public getNumber(): string | undefined {
|
public getNumber(): string | undefined {
|
||||||
const numberId = this.storage.get('number_id');
|
const numberId = this.storage.get('number_id');
|
||||||
if (numberId === undefined) return undefined;
|
if (numberId === undefined) return undefined;
|
||||||
|
|
3
ts/types/Storage.d.ts
vendored
3
ts/types/Storage.d.ts
vendored
|
@ -77,6 +77,9 @@ export type StorageAccessType = {
|
||||||
phoneNumberDiscoverability: PhoneNumberDiscoverability;
|
phoneNumberDiscoverability: PhoneNumberDiscoverability;
|
||||||
pinnedConversationIds: Array<string>;
|
pinnedConversationIds: Array<string>;
|
||||||
primarySendsSms: boolean;
|
primarySendsSms: boolean;
|
||||||
|
// Unlike `number_id` (which also includes device id) this field is only
|
||||||
|
// updated whenever we receive a new storage manifest
|
||||||
|
accountE164: string;
|
||||||
typingIndicators: boolean;
|
typingIndicators: boolean;
|
||||||
sealedSenderIndicators: boolean;
|
sealedSenderIndicators: boolean;
|
||||||
storageFetchComplete: boolean;
|
storageFetchComplete: boolean;
|
||||||
|
|
Loading…
Reference in a new issue