Introduce Service Id Types

Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
Fedor Indutny 2023-08-10 18:43:33 +02:00 committed by Jamie Kyle
parent 414c0a58d3
commit 366b875fd2
269 changed files with 5832 additions and 5550 deletions

View file

@ -23,9 +23,9 @@ message Envelope {
} }
optional Type type = 1; optional Type type = 1;
optional string sourceUuid = 11; optional string sourceServiceId = 11;
optional uint32 sourceDevice = 7; optional uint32 sourceDevice = 7;
optional string destinationUuid = 13; optional string destinationServiceId = 13;
// reserved 3; // formerly optional string relay = 3; // reserved 3; // formerly optional string relay = 3;
optional uint64 timestamp = 5; optional uint64 timestamp = 5;
// reserved 6; // formerly optional bytes legacyMessage = 6; // reserved 6; // formerly optional bytes legacyMessage = 6;
@ -204,7 +204,7 @@ message DataMessage {
optional uint64 id = 1; optional uint64 id = 1;
reserved /* author */ 2; // removed reserved /* author */ 2; // removed
optional string authorUuid = 5; optional string authorAci = 5;
optional string text = 3; optional string text = 3;
repeated QuotedAttachment attachments = 4; repeated QuotedAttachment attachments = 4;
repeated BodyRange bodyRanges = 6; repeated BodyRange bodyRanges = 6;
@ -298,7 +298,7 @@ message DataMessage {
optional string emoji = 1; optional string emoji = 1;
optional bool remove = 2; optional bool remove = 2;
reserved /* targetAuthorE164 */ 3; // removed reserved /* targetAuthorE164 */ 3; // removed
optional string targetAuthorUuid = 4; optional string targetAuthorAci = 4;
optional uint64 targetTimestamp = 5; optional uint64 targetTimestamp = 5;
} }
@ -320,7 +320,7 @@ message DataMessage {
optional uint32 length = 2; optional uint32 length = 2;
oneof associatedValue { oneof associatedValue {
string mentionUuid = 3; string mentionAci = 3;
Style style = 4; Style style = 4;
} }
} }
@ -330,7 +330,7 @@ message DataMessage {
} }
message StoryContext { message StoryContext {
optional string authorUuid = 1; optional string authorAci = 1;
optional uint64 sentTimestamp = 2; optional uint64 sentTimestamp = 2;
} }
@ -447,7 +447,7 @@ message Verified {
} }
optional string destination = 1; optional string destination = 1;
optional string destinationUuid = 5; optional string destinationAci = 5;
optional bytes identityKey = 2; optional bytes identityKey = 2;
optional State state = 3; optional State state = 3;
optional bytes nullMessage = 4; optional bytes nullMessage = 4;
@ -457,27 +457,18 @@ message SyncMessage {
message Sent { message Sent {
message UnidentifiedDeliveryStatus { message UnidentifiedDeliveryStatus {
optional string destination = 1; optional string destination = 1;
oneof destinationServiceId { optional string destinationServiceId = 3;
string destinationAci = 3;
string destinationPni = 4;
}
optional bool unidentified = 2; optional bool unidentified = 2;
} }
message StoryMessageRecipient { message StoryMessageRecipient {
oneof destinationServiceId { optional string destinationServiceId = 1;
string destinationAci = 1;
string destinationPni = 4;
}
repeated string distributionListIds = 2; repeated string distributionListIds = 2;
optional bool isAllowedToReply = 3; optional bool isAllowedToReply = 3;
} }
optional string destination = 1; optional string destination = 1;
oneof destinationServiceId { optional string destinationServiceId = 7;
string destinationAci = 7;
string destinationPni = 11;
}
optional uint64 timestamp = 2; optional uint64 timestamp = 2;
optional DataMessage message = 3; optional DataMessage message = 3;
optional uint64 expirationStartTimestamp = 4; optional uint64 expirationStartTimestamp = 4;
@ -495,7 +486,7 @@ message SyncMessage {
message Blocked { message Blocked {
repeated string numbers = 1; repeated string numbers = 1;
repeated string uuids = 3; repeated string acis = 3;
repeated bytes groupIds = 2; repeated bytes groupIds = 2;
} }
@ -519,13 +510,13 @@ message SyncMessage {
message Read { message Read {
optional string sender = 1; optional string sender = 1;
optional string senderUuid = 3; optional string senderAci = 3;
optional uint64 timestamp = 2; optional uint64 timestamp = 2;
} }
message Viewed { message Viewed {
optional string senderE164 = 1; optional string senderE164 = 1;
optional string senderUuid = 3; optional string senderAci = 3;
optional uint64 timestamp = 2; optional uint64 timestamp = 2;
} }
@ -551,7 +542,7 @@ message SyncMessage {
message ViewOnceOpen { message ViewOnceOpen {
optional string sender = 1; optional string sender = 1;
optional string senderUuid = 3; optional string senderAci = 3;
optional uint64 timestamp = 2; optional uint64 timestamp = 2;
} }
@ -565,7 +556,7 @@ message SyncMessage {
} }
optional string threadE164 = 1; optional string threadE164 = 1;
optional string threadUuid = 2; optional string threadAci = 2;
optional bytes groupId = 3; optional bytes groupId = 3;
optional Type type = 4; optional Type type = 4;
} }
@ -694,7 +685,7 @@ message ContactDetails {
} }
optional string number = 1; optional string number = 1;
optional string uuid = 9; optional string aci = 9;
optional string name = 2; optional string name = 2;
optional Avatar avatar = 3; optional Avatar avatar = 3;
optional string color = 4; optional string color = 4;

View file

@ -77,7 +77,7 @@ message ContactRecord {
UNVERIFIED = 2; UNVERIFIED = 2;
} }
optional string serviceUuid = 1; optional string aci = 1;
optional string serviceE164 = 2; optional string serviceE164 = 2;
optional string pni = 15; optional string pni = 15;
optional bytes profileKey = 3; optional bytes profileKey = 3;
@ -203,7 +203,7 @@ message AccountRecord {
message StoryDistributionListRecord { message StoryDistributionListRecord {
optional bytes identifier = 1; optional bytes identifier = 1;
optional string name = 2; optional string name = 2;
repeated string recipientUuids = 3; repeated string recipientServiceIds = 3;
optional uint64 deletedAtTimestamp = 4; optional uint64 deletedAtTimestamp = 4;
optional bool allowsReplies = 5; optional bool allowsReplies = 5;
optional bool isBlockList = 6; optional bool isBlockList = 6;

View file

@ -5,7 +5,6 @@ import { v4 as uuid } from 'uuid';
import { incrementMessageCounter } from '../util/incrementMessageCounter'; import { incrementMessageCounter } from '../util/incrementMessageCounter';
import { ReadStatus } from '../messages/MessageReadStatus'; import { ReadStatus } from '../messages/MessageReadStatus';
import { UUID } from '../types/UUID';
import { SendStatus } from '../messages/MessageSendState'; import { SendStatus } from '../messages/MessageSendState';
import { BodyRange } from '../types/BodyRange'; import { BodyRange } from '../types/BodyRange';
import { strictAssert } from '../util/assert'; import { strictAssert } from '../util/assert';
@ -42,7 +41,7 @@ export async function populateConversationWithMessages({
const logId = 'benchmarkConversationOpen/populateConversationWithMessages'; const logId = 'benchmarkConversationOpen/populateConversationWithMessages';
log.info(`${logId}: populating conversation`); log.info(`${logId}: populating conversation`);
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString(); const ourAci = window.textsecure.storage.user.getCheckedAci();
const conversation = window.ConversationController.get(conversationId); const conversation = window.ConversationController.get(conversationId);
strictAssert( strictAssert(
@ -71,7 +70,7 @@ export async function populateConversationWithMessages({
schemaVersion: window.Signal.Types.Message.CURRENT_SCHEMA_VERSION, schemaVersion: window.Signal.Types.Message.CURRENT_SCHEMA_VERSION,
received_at: incrementMessageCounter(), received_at: incrementMessageCounter(),
readStatus: isUnread ? ReadStatus.Unread : ReadStatus.Read, readStatus: isUnread ? ReadStatus.Unread : ReadStatus.Read,
sourceUuid: new UUID(isIncoming ? conversationId : ourUuid).toString(), sourceUuid: isIncoming ? conversation.getCheckedServiceId('CI') : ourAci,
...(isIncoming ...(isIncoming
? {} ? {}
: { : {
@ -87,7 +86,7 @@ export async function populateConversationWithMessages({
await window.Signal.Data.saveMessages(messages, { await window.Signal.Data.saveMessages(messages, {
forceSave: true, forceSave: true,
ourUuid, ourAci,
}); });
conversation.set('active_at', Date.now()); conversation.set('active_at', Date.now());

View file

@ -3,6 +3,7 @@
import { debounce, pick, uniq, without } from 'lodash'; import { debounce, pick, uniq, without } from 'lodash';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { v4 as generateUuid } from 'uuid';
import type { import type {
ConversationModelCollectionType, ConversationModelCollectionType,
@ -12,7 +13,6 @@ import type {
} from './model-types.d'; } from './model-types.d';
import type { ConversationModel } from './models/conversations'; import type { ConversationModel } from './models/conversations';
import type { MessageModel } from './models/messages'; import type { MessageModel } from './models/messages';
import type { UUIDStringType } from './types/UUID';
import dataInterface from './sql/Client'; import dataInterface from './sql/Client';
import * as log from './logging/log'; import * as log from './logging/log';
@ -23,11 +23,16 @@ import { assertDev, strictAssert } from './util/assert';
import { drop } from './util/drop'; import { drop } from './util/drop';
import { isGroupV1, isGroupV2 } from './util/whatTypeOfConversation'; import { isGroupV1, isGroupV2 } from './util/whatTypeOfConversation';
import { getConversationUnreadCountForAppBadge } from './util/getConversationUnreadCountForAppBadge'; import { getConversationUnreadCountForAppBadge } from './util/getConversationUnreadCountForAppBadge';
import { UUID, isValidUuid, UUIDKind } from './types/UUID'; import type { ServiceIdString } from './types/ServiceId';
import {
isServiceIdString,
normalizeAci,
normalizePni,
} from './types/ServiceId';
import { sleep } from './util/sleep'; import { sleep } from './util/sleep';
import { isNotNil } from './util/isNotNil'; import { isNotNil } from './util/isNotNil';
import { MINUTE, SECOND } from './util/durations'; import { MINUTE, SECOND } from './util/durations';
import { getUuidsForE164s } from './util/getUuidsForE164s'; import { getServiceIdsForE164s } from './util/getServiceIdsForE164s';
import { SIGNAL_ACI, SIGNAL_AVATAR_PATH } from './types/SignalConversation'; import { SIGNAL_ACI, SIGNAL_AVATAR_PATH } from './types/SignalConversation';
import { getTitleNoDefault } from './util/getTitle'; import { getTitleNoDefault } from './util/getTitle';
import * as StorageService from './services/storage'; import * as StorageService from './services/storage';
@ -35,7 +40,7 @@ import * as StorageService from './services/storage';
type ConvoMatchType = type ConvoMatchType =
| { | {
key: 'uuid' | 'pni'; key: 'uuid' | 'pni';
value: UUIDStringType | undefined; value: ServiceIdString | undefined;
match: ConversationModel | undefined; match: ConversationModel | undefined;
} }
| { | {
@ -118,7 +123,7 @@ const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
const { const {
getAllConversations, getAllConversations,
getAllGroupsInvolvingUuid, getAllGroupsInvolvingServiceId,
getMessagesBySentAt, getMessagesBySentAt,
migrateConversationMessages, migrateConversationMessages,
removeConversation, removeConversation,
@ -261,7 +266,7 @@ export class ConversationController {
return conversation; return conversation;
} }
const id = UUID.generate().toString(); const id = generateUuid();
if (type === 'group') { if (type === 'group') {
conversation = this._conversations.add({ conversation = this._conversations.add({
@ -273,7 +278,7 @@ export class ConversationController {
version: 2, version: 2,
...additionalInitialProps, ...additionalInitialProps,
}); });
} else if (isValidUuid(identifier)) { } else if (isServiceIdString(identifier)) {
conversation = this._conversations.add({ conversation = this._conversations.add({
id, id,
uuid: identifier, uuid: identifier,
@ -364,12 +369,8 @@ export class ConversationController {
getOurConversationId(): string | undefined { getOurConversationId(): string | undefined {
const e164 = window.textsecure.storage.user.getNumber(); const e164 = window.textsecure.storage.user.getNumber();
const aci = window.textsecure.storage.user const aci = window.textsecure.storage.user.getAci();
.getUuid(UUIDKind.ACI) const pni = window.textsecure.storage.user.getPni();
?.toString();
const pni = window.textsecure.storage.user
.getUuid(UUIDKind.PNI)
?.toString();
if (!e164 && !aci && !pni) { if (!e164 && !aci && !pni) {
return undefined; return undefined;
@ -483,9 +484,11 @@ export class ConversationController {
const aci = const aci =
providedAci && providedAci !== providedPni providedAci && providedAci !== providedPni
? UUID.cast(providedAci) ? normalizeAci(providedAci, 'maybeMergeContacts.aci')
: undefined; : undefined;
const pni = providedPni ? UUID.cast(providedPni) : undefined; const pni = providedPni
? normalizePni(providedPni, 'maybeMergeContacts.pni')
: undefined;
const mergePromises: Array<Promise<void>> = []; const mergePromises: Array<Promise<void>> = [];
if (!aci && !e164 && !pni) { if (!aci && !e164 && !pni) {
@ -1037,10 +1040,10 @@ export class ConversationController {
} }
const obsoleteId = obsolete.get('id'); const obsoleteId = obsolete.get('id');
const obsoleteUuid = obsolete.getUuid(); const obsoleteServiceId = obsolete.getServiceId();
const currentId = current.get('id'); const currentId = current.get('id');
if (conversationType === 'private' && obsoleteUuid) { if (conversationType === 'private' && obsoleteServiceId) {
if (!current.get('profileKey') && obsolete.get('profileKey')) { if (!current.get('profileKey') && obsolete.get('profileKey')) {
log.warn(`${logId}: Copying profile key from old to new contact`); log.warn(`${logId}: Copying profile key from old to new contact`);
@ -1060,16 +1063,18 @@ export class ConversationController {
log.warn( log.warn(
`${logId}: Delete all identity information tied to old conversationId` `${logId}: Delete all identity information tied to old conversationId`
); );
if (obsoleteUuid) { if (obsoleteServiceId) {
await window.textsecure.storage.protocol.removeIdentityKey( await window.textsecure.storage.protocol.removeIdentityKey(
obsoleteUuid obsoleteServiceId
); );
} }
log.warn( log.warn(
`${logId}: Ensure that all V1 groups have new conversationId instead of old` `${logId}: Ensure that all V1 groups have new conversationId instead of old`
); );
const groups = await this.getAllGroupsInvolvingUuid(obsoleteUuid); const groups = await this.getAllGroupsInvolvingServiceId(
obsoleteServiceId
);
groups.forEach(group => { groups.forEach(group => {
const members = group.get('members'); const members = group.get('members');
const withoutObsolete = without(members, obsoleteId); const withoutObsolete = without(members, obsoleteId);
@ -1178,10 +1183,10 @@ export class ConversationController {
return null; return null;
} }
async getAllGroupsInvolvingUuid( async getAllGroupsInvolvingServiceId(
uuid: UUID serviceId: ServiceIdString
): Promise<Array<ConversationModel>> { ): Promise<Array<ConversationModel>> {
const groups = await getAllGroupsInvolvingUuid(uuid.toString()); const groups = await getAllGroupsInvolvingServiceId(serviceId);
return groups.map(group => { return groups.map(group => {
const existing = this.get(group.id); const existing = this.get(group.id);
if (existing) { if (existing) {
@ -1278,7 +1283,7 @@ export class ConversationController {
async _forgetE164(e164: string): Promise<void> { async _forgetE164(e164: string): Promise<void> {
const { server } = window.textsecure; const { server } = window.textsecure;
strictAssert(server, 'Server must be initialized'); strictAssert(server, 'Server must be initialized');
const uuidMap = await getUuidsForE164s(server, [e164]); const uuidMap = await getServiceIdsForE164s(server, [e164]);
const pni = uuidMap.get(e164)?.pni; const pni = uuidMap.get(e164)?.pni;
@ -1362,7 +1367,7 @@ export class ConversationController {
// Clean up the conversations that have UUID as their e164. // Clean up the conversations that have UUID as their e164.
const e164 = conversation.get('e164'); const e164 = conversation.get('e164');
const uuid = conversation.get('uuid'); const uuid = conversation.get('uuid');
if (isValidUuid(e164) && uuid) { if (e164 && isServiceIdString(e164) && uuid) {
conversation.set({ e164: undefined }); conversation.set({ e164: undefined });
updateConversation(conversation.attributes); updateConversation(conversation.attributes);

View file

@ -13,6 +13,8 @@ import { getBytesSubarray } from './util/uuidToBytes';
export { HashType, CipherType }; export { HashType, CipherType };
export const UUID_BYTE_SIZE = 16;
const PROFILE_IV_LENGTH = 12; // bytes const PROFILE_IV_LENGTH = 12; // bytes
const PROFILE_KEY_LENGTH = 32; // bytes const PROFILE_KEY_LENGTH = 32; // bytes

View file

@ -27,37 +27,38 @@ import {
} from '@signalapp/libsignal-client'; } from '@signalapp/libsignal-client';
import { Address } from './types/Address'; import { Address } from './types/Address';
import { QualifiedAddress } from './types/QualifiedAddress'; import { QualifiedAddress } from './types/QualifiedAddress';
import type { UUID } from './types/UUID'; import type { ServiceIdString } from './types/ServiceId';
import { normalizeServiceId } from './types/ServiceId';
import type { Zone } from './util/Zone'; import type { Zone } from './util/Zone';
function encodeAddress(address: ProtocolAddress): Address { function encodeAddress(address: ProtocolAddress): Address {
const name = address.name(); const name = address.name();
const deviceId = address.deviceId(); const deviceId = address.deviceId();
return Address.create(name, deviceId); return Address.create(normalizeServiceId(name, 'encodeAddress'), deviceId);
} }
function toQualifiedAddress( function toQualifiedAddress(
ourUuid: UUID, ourServiceId: ServiceIdString,
address: ProtocolAddress address: ProtocolAddress
): QualifiedAddress { ): QualifiedAddress {
return new QualifiedAddress(ourUuid, encodeAddress(address)); return new QualifiedAddress(ourServiceId, encodeAddress(address));
} }
export type SessionsOptions = Readonly<{ export type SessionsOptions = Readonly<{
ourUuid: UUID; ourServiceId: ServiceIdString;
zone?: Zone; zone?: Zone;
}>; }>;
export class Sessions extends SessionStore { export class Sessions extends SessionStore {
private readonly ourUuid: UUID; private readonly ourServiceId: ServiceIdString;
private readonly zone: Zone | undefined; private readonly zone: Zone | undefined;
constructor({ ourUuid, zone }: SessionsOptions) { constructor({ ourServiceId, zone }: SessionsOptions) {
super(); super();
this.ourUuid = ourUuid; this.ourServiceId = ourServiceId;
this.zone = zone; this.zone = zone;
} }
@ -66,14 +67,14 @@ export class Sessions extends SessionStore {
record: SessionRecord record: SessionRecord
): Promise<void> { ): Promise<void> {
await window.textsecure.storage.protocol.storeSession( await window.textsecure.storage.protocol.storeSession(
toQualifiedAddress(this.ourUuid, address), toQualifiedAddress(this.ourServiceId, address),
record, record,
{ zone: this.zone } { zone: this.zone }
); );
} }
async getSession(name: ProtocolAddress): Promise<SessionRecord | null> { async getSession(name: ProtocolAddress): Promise<SessionRecord | null> {
const encodedAddress = toQualifiedAddress(this.ourUuid, name); const encodedAddress = toQualifiedAddress(this.ourServiceId, name);
const record = await window.textsecure.storage.protocol.loadSession( const record = await window.textsecure.storage.protocol.loadSession(
encodedAddress, encodedAddress,
{ zone: this.zone } { zone: this.zone }
@ -86,7 +87,7 @@ export class Sessions extends SessionStore {
addresses: Array<ProtocolAddress> addresses: Array<ProtocolAddress>
): Promise<Array<SessionRecord>> { ): Promise<Array<SessionRecord>> {
const encodedAddresses = addresses.map(addr => const encodedAddresses = addresses.map(addr =>
toQualifiedAddress(this.ourUuid, addr) toQualifiedAddress(this.ourServiceId, addr)
); );
return window.textsecure.storage.protocol.loadSessions(encodedAddresses, { return window.textsecure.storage.protocol.loadSessions(encodedAddresses, {
zone: this.zone, zone: this.zone,
@ -95,25 +96,25 @@ export class Sessions extends SessionStore {
} }
export type IdentityKeysOptions = Readonly<{ export type IdentityKeysOptions = Readonly<{
ourUuid: UUID; ourServiceId: ServiceIdString;
zone?: Zone; zone?: Zone;
}>; }>;
export class IdentityKeys extends IdentityKeyStore { export class IdentityKeys extends IdentityKeyStore {
private readonly ourUuid: UUID; private readonly ourServiceId: ServiceIdString;
private readonly zone: Zone | undefined; private readonly zone: Zone | undefined;
constructor({ ourUuid, zone }: IdentityKeysOptions) { constructor({ ourServiceId, zone }: IdentityKeysOptions) {
super(); super();
this.ourUuid = ourUuid; this.ourServiceId = ourServiceId;
this.zone = zone; this.zone = zone;
} }
async getIdentityKey(): Promise<PrivateKey> { async getIdentityKey(): Promise<PrivateKey> {
const keyPair = window.textsecure.storage.protocol.getIdentityKeyPair( const keyPair = window.textsecure.storage.protocol.getIdentityKeyPair(
this.ourUuid this.ourServiceId
); );
if (!keyPair) { if (!keyPair) {
throw new Error('IdentityKeyStore/getIdentityKey: No identity key!'); throw new Error('IdentityKeyStore/getIdentityKey: No identity key!');
@ -123,7 +124,7 @@ export class IdentityKeys extends IdentityKeyStore {
async getLocalRegistrationId(): Promise<number> { async getLocalRegistrationId(): Promise<number> {
const id = await window.textsecure.storage.protocol.getLocalRegistrationId( const id = await window.textsecure.storage.protocol.getLocalRegistrationId(
this.ourUuid this.ourServiceId
); );
if (!isNumber(id)) { if (!isNumber(id)) {
throw new Error( throw new Error(
@ -136,7 +137,7 @@ export class IdentityKeys extends IdentityKeyStore {
async getIdentity(address: ProtocolAddress): Promise<PublicKey | null> { async getIdentity(address: ProtocolAddress): Promise<PublicKey | null> {
const encodedAddress = encodeAddress(address); const encodedAddress = encodeAddress(address);
const key = await window.textsecure.storage.protocol.loadIdentityKey( const key = await window.textsecure.storage.protocol.loadIdentityKey(
encodedAddress.uuid encodedAddress.serviceId
); );
if (!key) { if (!key) {
@ -177,15 +178,15 @@ export class IdentityKeys extends IdentityKeyStore {
} }
export type PreKeysOptions = Readonly<{ export type PreKeysOptions = Readonly<{
ourUuid: UUID; ourServiceId: ServiceIdString;
}>; }>;
export class PreKeys extends PreKeyStore { export class PreKeys extends PreKeyStore {
private readonly ourUuid: UUID; private readonly ourServiceId: ServiceIdString;
constructor({ ourUuid }: PreKeysOptions) { constructor({ ourServiceId }: PreKeysOptions) {
super(); super();
this.ourUuid = ourUuid; this.ourServiceId = ourServiceId;
} }
async savePreKey(): Promise<void> { async savePreKey(): Promise<void> {
@ -194,7 +195,7 @@ export class PreKeys extends PreKeyStore {
async getPreKey(id: number): Promise<PreKeyRecord> { async getPreKey(id: number): Promise<PreKeyRecord> {
const preKey = await window.textsecure.storage.protocol.loadPreKey( const preKey = await window.textsecure.storage.protocol.loadPreKey(
this.ourUuid, this.ourServiceId,
id id
); );
@ -206,16 +207,18 @@ export class PreKeys extends PreKeyStore {
} }
async removePreKey(id: number): Promise<void> { async removePreKey(id: number): Promise<void> {
await window.textsecure.storage.protocol.removePreKeys(this.ourUuid, [id]); await window.textsecure.storage.protocol.removePreKeys(this.ourServiceId, [
id,
]);
} }
} }
export class KyberPreKeys extends KyberPreKeyStore { export class KyberPreKeys extends KyberPreKeyStore {
private readonly ourUuid: UUID; private readonly ourServiceId: ServiceIdString;
constructor({ ourUuid }: PreKeysOptions) { constructor({ ourServiceId }: PreKeysOptions) {
super(); super();
this.ourUuid = ourUuid; this.ourServiceId = ourServiceId;
} }
async saveKyberPreKey(): Promise<void> { async saveKyberPreKey(): Promise<void> {
@ -225,7 +228,7 @@ export class KyberPreKeys extends KyberPreKeyStore {
async getKyberPreKey(id: number): Promise<KyberPreKeyRecord> { async getKyberPreKey(id: number): Promise<KyberPreKeyRecord> {
const kyberPreKey = const kyberPreKey =
await window.textsecure.storage.protocol.loadKyberPreKey( await window.textsecure.storage.protocol.loadKyberPreKey(
this.ourUuid, this.ourServiceId,
id id
); );
@ -238,25 +241,25 @@ export class KyberPreKeys extends KyberPreKeyStore {
async markKyberPreKeyUsed(id: number): Promise<void> { async markKyberPreKeyUsed(id: number): Promise<void> {
await window.textsecure.storage.protocol.maybeRemoveKyberPreKey( await window.textsecure.storage.protocol.maybeRemoveKyberPreKey(
this.ourUuid, this.ourServiceId,
id id
); );
} }
} }
export type SenderKeysOptions = Readonly<{ export type SenderKeysOptions = Readonly<{
readonly ourUuid: UUID; readonly ourServiceId: ServiceIdString;
readonly zone: Zone | undefined; readonly zone: Zone | undefined;
}>; }>;
export class SenderKeys extends SenderKeyStore { export class SenderKeys extends SenderKeyStore {
private readonly ourUuid: UUID; private readonly ourServiceId: ServiceIdString;
readonly zone: Zone | undefined; readonly zone: Zone | undefined;
constructor({ ourUuid, zone }: SenderKeysOptions) { constructor({ ourServiceId, zone }: SenderKeysOptions) {
super(); super();
this.ourUuid = ourUuid; this.ourServiceId = ourServiceId;
this.zone = zone; this.zone = zone;
} }
@ -265,7 +268,7 @@ export class SenderKeys extends SenderKeyStore {
distributionId: Uuid, distributionId: Uuid,
record: SenderKeyRecord record: SenderKeyRecord
): Promise<void> { ): Promise<void> {
const encodedAddress = toQualifiedAddress(this.ourUuid, sender); const encodedAddress = toQualifiedAddress(this.ourServiceId, sender);
await window.textsecure.storage.protocol.saveSenderKey( await window.textsecure.storage.protocol.saveSenderKey(
encodedAddress, encodedAddress,
@ -279,7 +282,7 @@ export class SenderKeys extends SenderKeyStore {
sender: ProtocolAddress, sender: ProtocolAddress,
distributionId: Uuid distributionId: Uuid
): Promise<SenderKeyRecord | null> { ): Promise<SenderKeyRecord | null> {
const encodedAddress = toQualifiedAddress(this.ourUuid, sender); const encodedAddress = toQualifiedAddress(this.ourServiceId, sender);
const senderKey = await window.textsecure.storage.protocol.getSenderKey( const senderKey = await window.textsecure.storage.protocol.getSenderKey(
encodedAddress, encodedAddress,
@ -292,15 +295,15 @@ export class SenderKeys extends SenderKeyStore {
} }
export type SignedPreKeysOptions = Readonly<{ export type SignedPreKeysOptions = Readonly<{
ourUuid: UUID; ourServiceId: ServiceIdString;
}>; }>;
export class SignedPreKeys extends SignedPreKeyStore { export class SignedPreKeys extends SignedPreKeyStore {
private readonly ourUuid: UUID; private readonly ourServiceId: ServiceIdString;
constructor({ ourUuid }: SignedPreKeysOptions) { constructor({ ourServiceId }: SignedPreKeysOptions) {
super(); super();
this.ourUuid = ourUuid; this.ourServiceId = ourServiceId;
} }
async saveSignedPreKey(): Promise<void> { async saveSignedPreKey(): Promise<void> {
@ -310,7 +313,7 @@ export class SignedPreKeys extends SignedPreKeyStore {
async getSignedPreKey(id: number): Promise<SignedPreKeyRecord> { async getSignedPreKey(id: number): Promise<SignedPreKeyRecord> {
const signedPreKey = const signedPreKey =
await window.textsecure.storage.protocol.loadSignedPreKey( await window.textsecure.storage.protocol.loadSignedPreKey(
this.ourUuid, this.ourServiceId,
id id
); );

View file

@ -5,7 +5,7 @@ import { get, throttle } from 'lodash';
import type { WebAPIType } from './textsecure/WebAPI'; import type { WebAPIType } from './textsecure/WebAPI';
import * as log from './logging/log'; import * as log from './logging/log';
import type { UUIDStringType } from './types/UUID'; import type { AciString } from './types/ServiceId';
import { parseIntOrThrow } from './util/parseIntOrThrow'; import { parseIntOrThrow } from './util/parseIntOrThrow';
import { SECOND, HOUR } from './util/durations'; import { SECOND, HOUR } from './util/durations';
import * as Bytes from './Bytes'; import * as Bytes from './Bytes';
@ -162,18 +162,18 @@ export function getValue(name: ConfigKeyType): string | undefined {
export function isBucketValueEnabled( export function isBucketValueEnabled(
name: ConfigKeyType, name: ConfigKeyType,
e164: string | undefined, e164: string | undefined,
uuid: UUIDStringType | undefined aci: AciString | undefined
): boolean { ): boolean {
return innerIsBucketValueEnabled(name, getValue(name), e164, uuid); return innerIsBucketValueEnabled(name, getValue(name), e164, aci);
} }
export function innerIsBucketValueEnabled( export function innerIsBucketValueEnabled(
name: ConfigKeyType, name: ConfigKeyType,
flagValue: unknown, flagValue: unknown,
e164: string | undefined, e164: string | undefined,
uuid: UUIDStringType | undefined aci: AciString | undefined
): boolean { ): boolean {
if (e164 == null || uuid == null) { if (e164 == null || aci == null) {
return false; return false;
} }
@ -191,7 +191,7 @@ export function innerIsBucketValueEnabled(
return false; return false;
} }
const bucketValue = getBucketValue(uuid, name); const bucketValue = getBucketValue(aci, name);
return bucketValue < remoteConfigValue; return bucketValue < remoteConfigValue;
} }
@ -230,10 +230,10 @@ export function getCountryCodeValue(
return wildcard; return wildcard;
} }
export function getBucketValue(uuid: UUIDStringType, flagName: string): number { export function getBucketValue(aci: AciString, flagName: string): number {
const hashInput = Bytes.concatenate([ const hashInput = Bytes.concatenate([
Bytes.fromString(`${flagName}.`), Bytes.fromString(`${flagName}.`),
uuidToBytes(uuid), uuidToBytes(aci),
]); ]);
const hashResult = window.SignalContext.crypto.hash( const hashResult = window.SignalContext.crypto.hash(
HashType.size256, HashType.size256,

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,7 @@ import { bindActionCreators } from 'redux';
import { render } from 'react-dom'; import { render } from 'react-dom';
import { batch as batchDispatch } from 'react-redux'; import { batch as batchDispatch } from 'react-redux';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import { v4 as generateUuid } from 'uuid';
import * as Registration from './util/registration'; import * as Registration from './util/registration';
import MessageReceiver from './textsecure/MessageReceiver'; import MessageReceiver from './textsecure/MessageReceiver';
@ -39,7 +40,6 @@ import { drop } from './util/drop';
import { explodePromise } from './util/explodePromise'; import { explodePromise } from './util/explodePromise';
import { isWindowDragElement } from './util/isWindowDragElement'; import { isWindowDragElement } from './util/isWindowDragElement';
import { assertDev, strictAssert } from './util/assert'; import { assertDev, strictAssert } from './util/assert';
import { normalizeUuid } from './util/normalizeUuid';
import { filter } from './util/iterables'; import { filter } from './util/iterables';
import { isNotNil } from './util/isNotNil'; import { isNotNil } from './util/isNotNil';
import { isPnpEnabled } from './util/isPnpEnabled'; import { isPnpEnabled } from './util/isPnpEnabled';
@ -146,8 +146,13 @@ import {
import { themeChanged } from './shims/themeChanged'; import { themeChanged } from './shims/themeChanged';
import { createIPCEvents } from './util/createIPCEvents'; import { createIPCEvents } from './util/createIPCEvents';
import { RemoveAllConfiguration } from './types/RemoveAllConfiguration'; import { RemoveAllConfiguration } from './types/RemoveAllConfiguration';
import { isValidUuid, UUIDKind, UUID } from './types/UUID'; import type { ServiceIdString } from './types/ServiceId';
import type { TaggedUUIDStringType } from './types/UUID'; import {
ServiceIdKind,
isAciString,
isServiceIdString,
normalizeAci,
} from './types/ServiceId';
import * as log from './logging/log'; import * as log from './logging/log';
import { loadRecentEmojis } from './util/loadRecentEmojis'; import { loadRecentEmojis } from './util/loadRecentEmojis';
import { deleteAllLogs } from './util/deleteAllLogs'; import { deleteAllLogs } from './util/deleteAllLogs';
@ -594,9 +599,10 @@ export async function startApp(): Promise<void> {
window.textsecure.storage.protocol.on( window.textsecure.storage.protocol.on(
'lowKeys', 'lowKeys',
throttle( throttle(
(ourUuid: UUID) => { (ourServiceId: ServiceIdString) => {
const uuidKind = window.textsecure.storage.user.getOurUuidKind(ourUuid); const serviceIdKind =
drop(window.getAccountManager().maybeUpdateKeys(uuidKind)); window.textsecure.storage.user.getOurServiceIdKind(ourServiceId);
drop(window.getAccountManager().maybeUpdateKeys(serviceIdKind));
}, },
durations.MINUTE, durations.MINUTE,
{ trailing: true, leading: false } { trailing: true, leading: false }
@ -1322,12 +1328,8 @@ export async function startApp(): Promise<void> {
window.Whisper.events.on('userChanged', (reconnect = false) => { window.Whisper.events.on('userChanged', (reconnect = false) => {
const newDeviceId = window.textsecure.storage.user.getDeviceId(); const newDeviceId = window.textsecure.storage.user.getDeviceId();
const newNumber = window.textsecure.storage.user.getNumber(); const newNumber = window.textsecure.storage.user.getNumber();
const newACI = window.textsecure.storage.user const newACI = window.textsecure.storage.user.getAci();
.getUuid(UUIDKind.ACI) const newPNI = window.textsecure.storage.user.getPni();
?.toString();
const newPNI = window.textsecure.storage.user
.getUuid(UUIDKind.PNI)
?.toString();
const ourConversation = const ourConversation =
window.ConversationController.getOurConversation(); window.ConversationController.getOurConversation();
@ -1339,8 +1341,8 @@ export async function startApp(): Promise<void> {
ourConversationId: ourConversation?.get('id'), ourConversationId: ourConversation?.get('id'),
ourDeviceId: newDeviceId, ourDeviceId: newDeviceId,
ourNumber: newNumber, ourNumber: newNumber,
ourACI: newACI, ourAci: newACI,
ourPNI: newPNI, ourPni: newPNI,
regionCode: window.storage.get('regionCode'), regionCode: window.storage.get('regionCode'),
}); });
@ -1464,7 +1466,7 @@ export async function startApp(): Promise<void> {
log.info( log.info(
`Expiration start timestamp cleanup: Found ${messagesUnexpectedlyMissingExpirationStartTimestamp.length} messages for cleanup` `Expiration start timestamp cleanup: Found ${messagesUnexpectedlyMissingExpirationStartTimestamp.length} messages for cleanup`
); );
if (!window.textsecure.storage.user.getUuid()) { if (!window.textsecure.storage.user.getAci()) {
log.info( log.info(
"Expiration start timestamp cleanup: Cancelling update; we don't have our own UUID" "Expiration start timestamp cleanup: Cancelling update; we don't have our own UUID"
); );
@ -1495,7 +1497,7 @@ export async function startApp(): Promise<void> {
}); });
await window.Signal.Data.saveMessages(newMessageAttributes, { await window.Signal.Data.saveMessages(newMessageAttributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
log.info('Expiration start timestamp cleanup: complete'); log.info('Expiration start timestamp cleanup: complete');
@ -1537,7 +1539,7 @@ export async function startApp(): Promise<void> {
}); });
const isCoreDataValid = Boolean( const isCoreDataValid = Boolean(
window.textsecure.storage.user.getUuid() && window.textsecure.storage.user.getAci() &&
window.ConversationController.getOurConversation() window.ConversationController.getOurConversation()
); );
@ -1855,7 +1857,7 @@ export async function startApp(): Promise<void> {
const deviceId = window.textsecure.storage.user.getDeviceId(); const deviceId = window.textsecure.storage.user.getDeviceId();
if (!window.textsecure.storage.user.getUuid()) { if (!window.textsecure.storage.user.getAci()) {
log.error('UUID not captured during registration, unlinking'); log.error('UUID not captured during registration, unlinking');
return unlinkAndDisconnect(RemoveAllConfiguration.Full); return unlinkAndDisconnect(RemoveAllConfiguration.Full);
} }
@ -1875,7 +1877,7 @@ export async function startApp(): Promise<void> {
} }
} }
if (!window.textsecure.storage.user.getUuid(UUIDKind.PNI)) { if (!window.textsecure.storage.user.getPni()) {
log.error('PNI not captured during registration, unlinking softly'); log.error('PNI not captured during registration, unlinking softly');
return unlinkAndDisconnect(RemoveAllConfiguration.Soft); return unlinkAndDisconnect(RemoveAllConfiguration.Soft);
} }
@ -2170,7 +2172,7 @@ export async function startApp(): Promise<void> {
function onTyping(ev: TypingEvent): void { function onTyping(ev: TypingEvent): void {
// Note: this type of message is automatically removed from cache in MessageReceiver // Note: this type of message is automatically removed from cache in MessageReceiver
const { typing, sender, senderUuid, senderDevice } = ev; const { typing, sender, senderAci, senderDevice } = ev;
const { groupV2Id, started } = typing || {}; const { groupV2Id, started } = typing || {};
// We don't do anything with incoming typing messages if the setting is disabled // We don't do anything with incoming typing messages if the setting is disabled
@ -2183,7 +2185,7 @@ export async function startApp(): Promise<void> {
const { conversation: senderConversation } = const { conversation: senderConversation } =
window.ConversationController.maybeMergeContacts({ window.ConversationController.maybeMergeContacts({
e164: sender, e164: sender,
aci: senderUuid, aci: senderAci,
reason: `onTyping(${typing.timestamp})`, reason: `onTyping(${typing.timestamp})`,
}); });
@ -2202,19 +2204,19 @@ export async function startApp(): Promise<void> {
} }
if (!conversation) { if (!conversation) {
log.warn( log.warn(
`onTyping: Did not find conversation for typing indicator (groupv2(${groupV2Id}), ${sender}, ${senderUuid})` `onTyping: Did not find conversation for typing indicator (groupv2(${groupV2Id}), ${sender}, ${senderAci})`
); );
return; return;
} }
const ourACI = window.textsecure.storage.user.getUuid(UUIDKind.ACI); const ourAci = window.textsecure.storage.user.getAci();
const ourPNI = window.textsecure.storage.user.getUuid(UUIDKind.PNI); const ourPni = window.textsecure.storage.user.getPni();
// We drop typing notifications in groups we're not a part of // We drop typing notifications in groups we're not a part of
if ( if (
!isDirectConversation(conversation.attributes) && !isDirectConversation(conversation.attributes) &&
!(ourACI && conversation.hasMember(ourACI)) && !(ourAci && conversation.hasMember(ourAci)) &&
!(ourPNI && conversation.hasMember(ourPNI)) !(ourPni && conversation.hasMember(ourPni))
) { ) {
log.warn( log.warn(
`Received typing indicator for group ${conversation.idForLogging()}, which we're not a part of. Dropping.` `Received typing indicator for group ${conversation.idForLogging()}, which we're not a part of. Dropping.`
@ -2353,12 +2355,15 @@ export async function startApp(): Promise<void> {
}: EnvelopeUnsealedEvent): Promise<void> { }: EnvelopeUnsealedEvent): Promise<void> {
throttledSetInboxEnvelopeTimestamp(envelope.serverTimestamp); throttledSetInboxEnvelopeTimestamp(envelope.serverTimestamp);
const ourUuid = window.textsecure.storage.user.getUuid()?.toString(); const ourAci = window.textsecure.storage.user.getAci();
if (envelope.sourceUuid && envelope.sourceUuid !== ourUuid) { if (
envelope.sourceServiceId !== ourAci &&
isAciString(envelope.sourceServiceId)
) {
const { mergePromises, conversation } = const { mergePromises, conversation } =
window.ConversationController.maybeMergeContacts({ window.ConversationController.maybeMergeContacts({
e164: envelope.source, e164: envelope.source,
aci: envelope.sourceUuid, aci: envelope.sourceServiceId,
reason: `onEnvelopeUnsealed(${envelope.timestamp})`, reason: `onEnvelopeUnsealed(${envelope.timestamp})`,
}); });
@ -2382,9 +2387,7 @@ export async function startApp(): Promise<void> {
message: data.message, message: data.message,
// 'message' event: for 1:1 converations, the conversation is same as sender // 'message' event: for 1:1 converations, the conversation is same as sender
destination: data.source, destination: data.source,
destinationUuid: { destinationServiceId: data.sourceAci,
aci: data.sourceUuid,
},
}); });
const { PROFILE_KEY_UPDATE } = Proto.DataMessage.Flags; const { PROFILE_KEY_UPDATE } = Proto.DataMessage.Flags;
@ -2404,11 +2407,14 @@ export async function startApp(): Promise<void> {
const sender = getContact(message.attributes); const sender = getContact(message.attributes);
strictAssert(sender, 'MessageModel has no sender'); strictAssert(sender, 'MessageModel has no sender');
const uuidKind = window.textsecure.storage.user.getOurUuidKind( const serviceIdKind = window.textsecure.storage.user.getOurServiceIdKind(
new UUID(data.destinationUuid) data.destinationServiceId
); );
if (uuidKind === UUIDKind.PNI && !sender.get('shareMyPhoneNumber')) { if (
serviceIdKind === ServiceIdKind.PNI &&
!sender.get('shareMyPhoneNumber')
) {
log.info( log.info(
'onMessageReceived: setting shareMyPhoneNumber ' + 'onMessageReceived: setting shareMyPhoneNumber ' +
`for ${sender.idForLogging()}` `for ${sender.idForLogging()}`
@ -2428,12 +2434,12 @@ export async function startApp(): Promise<void> {
if (data.message.reaction) { if (data.message.reaction) {
strictAssert( strictAssert(
data.message.reaction.targetAuthorUuid, data.message.reaction.targetAuthorAci,
'Reaction without targetAuthorUuid' 'Reaction without targetAuthorAci'
); );
const targetAuthorUuid = normalizeUuid( const targetAuthorAci = normalizeAci(
data.message.reaction.targetAuthorUuid, data.message.reaction.targetAuthorAci,
'DataMessage.Reaction.targetAuthorUuid' 'DataMessage.Reaction.targetAuthorAci'
); );
const { reaction, timestamp } = data.message; const { reaction, timestamp } = data.message;
@ -2448,11 +2454,12 @@ export async function startApp(): Promise<void> {
reaction.targetTimestamp, reaction.targetTimestamp,
'Reaction without targetTimestamp' 'Reaction without targetTimestamp'
); );
const fromConversation = window.ConversationController.lookupOrCreate({ const { conversation: fromConversation } =
e164: data.source, window.ConversationController.maybeMergeContacts({
uuid: data.sourceUuid, e164: data.source,
reason: 'onMessageReceived:reaction', aci: data.sourceAci,
}); reason: 'onMessageReceived:reaction',
});
strictAssert(fromConversation, 'Reaction without fromConversation'); strictAssert(fromConversation, 'Reaction without fromConversation');
log.info('Queuing incoming reaction for', reaction.targetTimestamp); log.info('Queuing incoming reaction for', reaction.targetTimestamp);
@ -2462,7 +2469,7 @@ export async function startApp(): Promise<void> {
remove: reaction.remove, remove: reaction.remove,
source: ReactionSource.FromSomeoneElse, source: ReactionSource.FromSomeoneElse,
storyReactionMessage: message, storyReactionMessage: message,
targetAuthorUuid, targetAuthorUuid: targetAuthorAci,
targetTimestamp: reaction.targetTimestamp, targetTimestamp: reaction.targetTimestamp,
timestamp, timestamp,
}; };
@ -2483,11 +2490,12 @@ export async function startApp(): Promise<void> {
'Delete missing targetSentTimestamp' 'Delete missing targetSentTimestamp'
); );
strictAssert(data.serverTimestamp, 'Delete missing serverTimestamp'); strictAssert(data.serverTimestamp, 'Delete missing serverTimestamp');
const fromConversation = window.ConversationController.lookupOrCreate({ const { conversation: fromConversation } =
e164: data.source, window.ConversationController.maybeMergeContacts({
uuid: data.sourceUuid, e164: data.source,
reason: 'onMessageReceived:delete', aci: data.sourceAci,
}); reason: 'onMessageReceived:delete',
});
strictAssert(fromConversation, 'Delete missing fromConversation'); strictAssert(fromConversation, 'Delete missing fromConversation');
const attributes: DeleteAttributesType = { const attributes: DeleteAttributesType = {
@ -2506,11 +2514,12 @@ export async function startApp(): Promise<void> {
const { editedMessageTimestamp } = data.message; const { editedMessageTimestamp } = data.message;
strictAssert(editedMessageTimestamp, 'Edit missing targetSentTimestamp'); strictAssert(editedMessageTimestamp, 'Edit missing targetSentTimestamp');
const fromConversation = window.ConversationController.lookupOrCreate({ const { conversation: fromConversation } =
e164: data.source, window.ConversationController.maybeMergeContacts({
uuid: data.sourceUuid, aci: data.sourceAci,
reason: 'onMessageReceived:edit', e164: data.source,
}); reason: 'onMessageReceived:edit',
});
strictAssert(fromConversation, 'Edit missing fromConversation'); strictAssert(fromConversation, 'Edit missing fromConversation');
log.info('Queuing incoming edit for', { log.info('Queuing incoming edit for', {
@ -2547,7 +2556,7 @@ export async function startApp(): Promise<void> {
confirm, confirm,
}: ProfileKeyUpdateEvent): Promise<void> { }: ProfileKeyUpdateEvent): Promise<void> {
const { conversation } = window.ConversationController.maybeMergeContacts({ const { conversation } = window.ConversationController.maybeMergeContacts({
aci: data.sourceUuid, aci: data.sourceAci,
e164: data.source, e164: data.source,
reason: 'onProfileKeyUpdate', reason: 'onProfileKeyUpdate',
}); });
@ -2560,7 +2569,7 @@ export async function startApp(): Promise<void> {
log.info( log.info(
'onProfileKeyUpdate: updating profileKey for', 'onProfileKeyUpdate: updating profileKey for',
data.sourceUuid, data.sourceAci,
data.source data.source
); );
@ -2617,10 +2626,10 @@ export async function startApp(): Promise<void> {
unidentifiedStatus.reduce( unidentifiedStatus.reduce(
( (
result: SendStateByConversationId, result: SendStateByConversationId,
{ destinationUuid, destination, isAllowedToReplyToStory } { destinationServiceId, destination, isAllowedToReplyToStory }
) => { ) => {
const conversation = window.ConversationController.get( const conversation = window.ConversationController.get(
destinationUuid?.aci || destinationUuid?.pni || destination destinationServiceId || destination
); );
if (!conversation || conversation.id === ourId) { if (!conversation || conversation.id === ourId) {
return result; return result;
@ -2647,17 +2656,12 @@ export async function startApp(): Promise<void> {
if (unidentifiedStatus.length) { if (unidentifiedStatus.length) {
unidentifiedDeliveries = unidentifiedStatus unidentifiedDeliveries = unidentifiedStatus
.filter(item => Boolean(item.unidentified)) .filter(item => Boolean(item.unidentified))
.map( .map(item => item.destinationServiceId || item.destination)
item =>
item.destinationUuid?.aci ||
item.destinationUuid?.pni ||
item.destination
)
.filter(isNotNil); .filter(isNotNil);
} }
const partialMessage: MessageAttributesType = { const partialMessage: MessageAttributesType = {
id: UUID.generate().toString(), id: generateUuid(),
canReplyToStory: data.message.isStory canReplyToStory: data.message.isStory
? data.message.canReplyToStory ? data.message.canReplyToStory
: undefined, : undefined,
@ -2675,7 +2679,7 @@ export async function startApp(): Promise<void> {
serverTimestamp: data.serverTimestamp, serverTimestamp: data.serverTimestamp,
source: window.textsecure.storage.user.getNumber(), source: window.textsecure.storage.user.getNumber(),
sourceDevice: data.device, sourceDevice: data.device,
sourceUuid: window.textsecure.storage.user.getUuid()?.toString(), sourceUuid: window.textsecure.storage.user.getAci(),
timestamp, timestamp,
type: data.message.isStory ? 'story' : 'outgoing', type: data.message.isStory ? 'story' : 'outgoing',
storyDistributionListId: data.storyDistributionListId, storyDistributionListId: data.storyDistributionListId,
@ -2689,11 +2693,11 @@ export async function startApp(): Promise<void> {
const getMessageDescriptor = ({ const getMessageDescriptor = ({
message, message,
destination, destination,
destinationUuid, destinationServiceId,
}: { }: {
message: ProcessedDataMessage; message: ProcessedDataMessage;
destination?: string; destination?: string;
destinationUuid?: TaggedUUIDStringType; destinationServiceId?: ServiceIdString;
}): MessageDescriptor => { }): MessageDescriptor => {
if (message.groupV2) { if (message.groupV2) {
const { id } = message.groupV2; const { id } = message.groupV2;
@ -2734,7 +2738,7 @@ export async function startApp(): Promise<void> {
} }
const conversation = window.ConversationController.get( const conversation = window.ConversationController.get(
destinationUuid?.aci || destinationUuid?.pni || destination destinationServiceId || destination
); );
strictAssert(conversation, 'Destination conversation cannot be created'); strictAssert(conversation, 'Destination conversation cannot be created');
@ -2751,7 +2755,7 @@ export async function startApp(): Promise<void> {
const { data, confirm } = event; const { data, confirm } = event;
const source = window.textsecure.storage.user.getNumber(); const source = window.textsecure.storage.user.getNumber();
const sourceUuid = window.textsecure.storage.user.getUuid()?.toString(); const sourceUuid = window.textsecure.storage.user.getAci();
strictAssert(source && sourceUuid, 'Missing user number and uuid'); strictAssert(source && sourceUuid, 'Missing user number and uuid');
const messageDescriptor = getMessageDescriptor({ const messageDescriptor = getMessageDescriptor({
@ -2773,18 +2777,18 @@ export async function startApp(): Promise<void> {
if (data.message.reaction) { if (data.message.reaction) {
strictAssert( strictAssert(
data.message.reaction.targetAuthorUuid, data.message.reaction.targetAuthorAci,
'Reaction without targetAuthorUuid' 'Reaction without targetAuthorAci'
); );
const targetAuthorUuid = normalizeUuid( const targetAuthorAci = normalizeAci(
data.message.reaction.targetAuthorUuid, data.message.reaction.targetAuthorAci,
'DataMessage.Reaction.targetAuthorUuid' 'DataMessage.Reaction.targetAuthorAci'
); );
const { reaction, timestamp } = data.message; const { reaction, timestamp } = data.message;
strictAssert( strictAssert(
reaction.targetTimestamp, reaction.targetTimestamp,
'Reaction without targetAuthorUuid' 'Reaction without targetAuthorAci'
); );
if (!isValidReactionEmoji(reaction.emoji)) { if (!isValidReactionEmoji(reaction.emoji)) {
@ -2800,7 +2804,7 @@ export async function startApp(): Promise<void> {
remove: reaction.remove, remove: reaction.remove,
source: ReactionSource.FromSync, source: ReactionSource.FromSync,
storyReactionMessage: message, storyReactionMessage: message,
targetAuthorUuid, targetAuthorUuid: targetAuthorAci,
targetTimestamp: reaction.targetTimestamp, targetTimestamp: reaction.targetTimestamp,
timestamp, timestamp,
}; };
@ -2882,7 +2886,7 @@ export async function startApp(): Promise<void> {
`Did not receive receivedAtCounter for message: ${data.timestamp}` `Did not receive receivedAtCounter for message: ${data.timestamp}`
); );
const partialMessage: MessageAttributesType = { const partialMessage: MessageAttributesType = {
id: UUID.generate().toString(), id: generateUuid(),
canReplyToStory: data.message.isStory canReplyToStory: data.message.isStory
? data.message.canReplyToStory ? data.message.canReplyToStory
: undefined, : undefined,
@ -2896,7 +2900,7 @@ export async function startApp(): Promise<void> {
serverTimestamp: data.serverTimestamp, serverTimestamp: data.serverTimestamp,
source: data.source, source: data.source,
sourceDevice: data.sourceDevice, sourceDevice: data.sourceDevice,
sourceUuid: data.sourceUuid ? UUID.cast(data.sourceUuid) : undefined, sourceUuid: data.sourceAci,
timestamp: data.timestamp, timestamp: data.timestamp,
type: data.message.isStory ? 'story' : 'incoming', type: data.message.isStory ? 'story' : 'incoming',
unidentifiedDeliveryReceived: data.unidentifiedDeliveryReceived, unidentifiedDeliveryReceived: data.unidentifiedDeliveryReceived,
@ -3033,14 +3037,14 @@ export async function startApp(): Promise<void> {
function onViewOnceOpenSync(ev: ViewOnceOpenSyncEvent): void { function onViewOnceOpenSync(ev: ViewOnceOpenSyncEvent): void {
ev.confirm(); ev.confirm();
const { source, sourceUuid, timestamp } = ev; const { source, sourceAci, timestamp } = ev;
log.info(`view once open sync ${source} ${timestamp}`); log.info(`view once open sync ${source} ${timestamp}`);
strictAssert(sourceUuid, 'ViewOnceOpen without sourceUuid'); strictAssert(sourceAci, 'ViewOnceOpen without sourceAci');
strictAssert(timestamp, 'ViewOnceOpen without timestamp'); strictAssert(timestamp, 'ViewOnceOpen without timestamp');
const attributes: ViewOnceOpenSyncAttributesType = { const attributes: ViewOnceOpenSyncAttributesType = {
source, source,
sourceUuid, sourceAci,
timestamp, timestamp,
}; };
const sync = ViewOnceOpenSyncs.getSingleton().add(attributes); const sync = ViewOnceOpenSyncs.getSingleton().add(attributes);
@ -3058,9 +3062,9 @@ export async function startApp(): Promise<void> {
switch (eventType) { switch (eventType) {
case FETCH_LATEST_ENUM.LOCAL_PROFILE: { case FETCH_LATEST_ENUM.LOCAL_PROFILE: {
log.info('onFetchLatestSync: fetching latest local profile'); log.info('onFetchLatestSync: fetching latest local profile');
const ourUuid = window.textsecure.storage.user.getUuid()?.toString(); const ourAci = window.textsecure.storage.user.getAci();
const ourE164 = window.textsecure.storage.user.getNumber(); const ourE164 = window.textsecure.storage.user.getNumber();
await getProfile(ourUuid, ourE164); await getProfile(ourAci, ourE164);
break; break;
} }
case FETCH_LATEST_ENUM.STORAGE_MANIFEST: case FETCH_LATEST_ENUM.STORAGE_MANIFEST:
@ -3111,12 +3115,11 @@ export async function startApp(): Promise<void> {
function onMessageRequestResponse(ev: MessageRequestResponseEvent): void { function onMessageRequestResponse(ev: MessageRequestResponseEvent): void {
ev.confirm(); ev.confirm();
const { threadE164, threadUuid, groupV2Id, messageRequestResponseType } = const { threadE164, threadAci, groupV2Id, messageRequestResponseType } = ev;
ev;
log.info('onMessageRequestResponse', { log.info('onMessageRequestResponse', {
threadE164, threadE164,
threadUuid, threadAci,
groupV2Id: `groupv2(${groupV2Id})`, groupV2Id: `groupv2(${groupV2Id})`,
messageRequestResponseType, messageRequestResponseType,
}); });
@ -3128,7 +3131,7 @@ export async function startApp(): Promise<void> {
const attributes: MessageRequestAttributesType = { const attributes: MessageRequestAttributesType = {
threadE164, threadE164,
threadUuid, threadAci,
groupV2Id, groupV2Id,
type: messageRequestResponseType, type: messageRequestResponseType,
}; };
@ -3166,19 +3169,19 @@ export async function startApp(): Promise<void> {
envelopeTimestamp, envelopeTimestamp,
timestamp, timestamp,
source, source,
sourceUuid, sourceServiceId,
sourceDevice, sourceDevice,
wasSentEncrypted, wasSentEncrypted,
} = event.receipt; } = event.receipt;
const { conversation: sourceConversation } = const sourceConversation = window.ConversationController.lookupOrCreate({
window.ConversationController.maybeMergeContacts({ uuid: sourceServiceId,
aci: sourceUuid, e164: source,
e164: source, reason: `onReadOrViewReceipt(${envelopeTimestamp})`,
reason: `onReadOrViewReceipt(${envelopeTimestamp})`, });
}); strictAssert(sourceConversation, 'Failed to create conversation');
log.info( log.info(
logTitle, logTitle,
`${sourceUuid || source}.${sourceDevice}`, `${sourceServiceId || source}.${sourceDevice}`,
envelopeTimestamp, envelopeTimestamp,
'for sent message', 'for sent message',
timestamp timestamp
@ -3187,8 +3190,8 @@ export async function startApp(): Promise<void> {
event.confirm(); event.confirm();
strictAssert( strictAssert(
isValidUuid(sourceUuid), isServiceIdString(sourceServiceId),
'onReadOrViewReceipt: Missing sourceUuid' 'onReadOrViewReceipt: Missing sourceServiceId'
); );
strictAssert(sourceDevice, 'onReadOrViewReceipt: Missing sourceDevice'); strictAssert(sourceDevice, 'onReadOrViewReceipt: Missing sourceDevice');
@ -3196,7 +3199,7 @@ export async function startApp(): Promise<void> {
messageSentAt: timestamp, messageSentAt: timestamp,
receiptTimestamp: envelopeTimestamp, receiptTimestamp: envelopeTimestamp,
sourceConversationId: sourceConversation.id, sourceConversationId: sourceConversation.id,
sourceUuid, sourceServiceId,
sourceDevice, sourceDevice,
type, type,
wasSentEncrypted, wasSentEncrypted,
@ -3208,19 +3211,20 @@ export async function startApp(): Promise<void> {
} }
function onReadSync(ev: ReadSyncEvent): Promise<void> { function onReadSync(ev: ReadSyncEvent): Promise<void> {
const { envelopeTimestamp, sender, senderUuid, timestamp } = ev.read; const { envelopeTimestamp, sender, senderAci, timestamp } = ev.read;
const readAt = envelopeTimestamp; const readAt = envelopeTimestamp;
const senderConversation = window.ConversationController.lookupOrCreate({ const { conversation: senderConversation } =
e164: sender, window.ConversationController.maybeMergeContacts({
uuid: senderUuid, aci: senderAci,
reason: 'onReadSync', e164: sender,
}); reason: 'onReadSync',
});
const senderId = senderConversation?.id; const senderId = senderConversation?.id;
log.info( log.info(
'read sync', 'read sync',
sender, sender,
senderUuid, senderAci,
envelopeTimestamp, envelopeTimestamp,
senderId, senderId,
'for message', 'for message',
@ -3228,13 +3232,13 @@ export async function startApp(): Promise<void> {
); );
strictAssert(senderId, 'onReadSync missing senderId'); strictAssert(senderId, 'onReadSync missing senderId');
strictAssert(senderUuid, 'onReadSync missing senderUuid'); strictAssert(senderAci, 'onReadSync missing senderAci');
strictAssert(timestamp, 'onReadSync missing timestamp'); strictAssert(timestamp, 'onReadSync missing timestamp');
const attributes: ReadSyncAttributesType = { const attributes: ReadSyncAttributesType = {
senderId, senderId,
sender, sender,
senderUuid, senderAci,
timestamp, timestamp,
readAt, readAt,
}; };
@ -3248,18 +3252,19 @@ export async function startApp(): Promise<void> {
} }
function onViewSync(ev: ViewSyncEvent): Promise<void> { function onViewSync(ev: ViewSyncEvent): Promise<void> {
const { envelopeTimestamp, senderE164, senderUuid, timestamp } = ev.view; const { envelopeTimestamp, senderE164, senderAci, timestamp } = ev.view;
const senderConversation = window.ConversationController.lookupOrCreate({ const { conversation: senderConversation } =
e164: senderE164, window.ConversationController.maybeMergeContacts({
uuid: senderUuid, e164: senderE164,
reason: 'onViewSync', aci: senderAci,
}); reason: 'onViewSync',
});
const senderId = senderConversation?.id; const senderId = senderConversation?.id;
log.info( log.info(
'view sync', 'view sync',
senderE164, senderE164,
senderUuid, senderAci,
envelopeTimestamp, envelopeTimestamp,
senderId, senderId,
'for message', 'for message',
@ -3267,13 +3272,13 @@ export async function startApp(): Promise<void> {
); );
strictAssert(senderId, 'onViewSync missing senderId'); strictAssert(senderId, 'onViewSync missing senderId');
strictAssert(senderUuid, 'onViewSync missing senderUuid'); strictAssert(senderAci, 'onViewSync missing senderAci');
strictAssert(timestamp, 'onViewSync missing timestamp'); strictAssert(timestamp, 'onViewSync missing timestamp');
const attributes: ViewSyncAttributesType = { const attributes: ViewSyncAttributesType = {
senderId, senderId,
senderE164, senderE164,
senderUuid, senderAci,
timestamp, timestamp,
viewedAt: envelopeTimestamp, viewedAt: envelopeTimestamp,
}; };
@ -3290,7 +3295,7 @@ export async function startApp(): Promise<void> {
const { deliveryReceipt } = ev; const { deliveryReceipt } = ev;
const { const {
envelopeTimestamp, envelopeTimestamp,
sourceUuid, sourceServiceId,
source, source,
sourceDevice, sourceDevice,
timestamp, timestamp,
@ -3299,16 +3304,15 @@ export async function startApp(): Promise<void> {
ev.confirm(); ev.confirm();
const { conversation: sourceConversation } = const sourceConversation = window.ConversationController.lookupOrCreate({
window.ConversationController.maybeMergeContacts({ uuid: sourceServiceId,
aci: sourceUuid, e164: source,
e164: source, reason: `onDeliveryReceipt(${envelopeTimestamp})`,
reason: `onDeliveryReceipt(${envelopeTimestamp})`, });
});
log.info( log.info(
'delivery receipt from', 'delivery receipt from',
`${sourceUuid || source}.${sourceDevice}`, `${sourceServiceId || source}.${sourceDevice}`,
envelopeTimestamp, envelopeTimestamp,
'for sent message', 'for sent message',
timestamp, timestamp,
@ -3320,8 +3324,8 @@ export async function startApp(): Promise<void> {
'onDeliveryReceipt: missing envelopeTimestamp' 'onDeliveryReceipt: missing envelopeTimestamp'
); );
strictAssert( strictAssert(
isValidUuid(sourceUuid), isServiceIdString(sourceServiceId),
'onDeliveryReceipt: missing valid sourceUuid' 'onDeliveryReceipt: missing valid sourceServiceId'
); );
strictAssert(sourceDevice, 'onDeliveryReceipt: missing sourceDevice'); strictAssert(sourceDevice, 'onDeliveryReceipt: missing sourceDevice');
@ -3329,7 +3333,7 @@ export async function startApp(): Promise<void> {
messageSentAt: timestamp, messageSentAt: timestamp,
receiptTimestamp: envelopeTimestamp, receiptTimestamp: envelopeTimestamp,
sourceConversationId: sourceConversation?.id, sourceConversationId: sourceConversation?.id,
sourceUuid, sourceServiceId,
sourceDevice, sourceDevice,
type: MessageReceiptType.Delivery, type: MessageReceiptType.Delivery,
wasSentEncrypted, wasSentEncrypted,

View file

@ -18,6 +18,7 @@ import {
import type { ConversationTypeType } from '../state/ducks/conversations'; import type { ConversationTypeType } from '../state/ducks/conversations';
import type { AvatarColorType } from '../types/Colors'; import type { AvatarColorType } from '../types/Colors';
import { AvatarColors } from '../types/Colors'; import { AvatarColors } from '../types/Colors';
import { generateAci } from '../types/ServiceId';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGroupCallVideoFrameSource'; import { fakeGetGroupCallVideoFrameSource } from '../test-both/helpers/fakeGetGroupCallVideoFrameSource';
import { setupI18n } from '../util/setupI18n'; import { setupI18n } from '../util/setupI18n';
@ -89,7 +90,7 @@ const createProps = (storyProps: Partial<PropsType> = {}): PropsType => ({
), ),
title: text('Caller Title', 'Morty Smith'), title: text('Caller Title', 'Morty Smith'),
}), }),
uuid: 'cb0dd0c8-7393-41e9-a0aa-d631c4109541', uuid: generateAci(),
}, },
notifyForCall: action('notify-for-call'), notifyForCall: action('notify-for-call'),
openSystemPreferencesAction: action('open-system-preferences-action'), openSystemPreferencesAction: action('open-system-preferences-action'),

View file

@ -14,6 +14,7 @@ import {
GroupCallConnectionState, GroupCallConnectionState,
GroupCallJoinState, GroupCallJoinState,
} from '../types/Calling'; } from '../types/Calling';
import { generateAci } from '../types/ServiceId';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import { AvatarColors } from '../types/Colors'; import { AvatarColors } from '../types/Colors';
import type { PropsType } from './CallScreen'; import type { PropsType } from './CallScreen';
@ -174,7 +175,7 @@ const createProps = (
name: 'Morty Smith', name: 'Morty Smith',
profileName: 'Morty Smith', profileName: 'Morty Smith',
title: 'Morty Smith', title: 'Morty Smith',
uuid: '3c134598-eecb-42ab-9ad3-2b0873f771b2', uuid: generateAci(),
}), }),
openSystemPreferencesAction: action('open-system-preferences-action'), openSystemPreferencesAction: action('open-system-preferences-action'),
setGroupCallVideoRequest: action('set-group-call-video-request'), setGroupCallVideoRequest: action('set-group-call-video-request'),
@ -311,7 +312,7 @@ export function GroupCall1(): JSX.Element {
videoAspectRatio: 1.3, videoAspectRatio: 1.3,
...getDefaultConversation({ ...getDefaultConversation({
isBlocked: false, isBlocked: false,
uuid: '72fa60e5-25fb-472d-8a56-e56867c57dda', uuid: generateAci(),
title: 'Tyler', title: 'Tyler',
}), }),
}, },
@ -379,7 +380,7 @@ export function GroupCallReconnecting(): JSX.Element {
...getDefaultConversation({ ...getDefaultConversation({
isBlocked: false, isBlocked: false,
title: 'Tyler', title: 'Tyler',
uuid: '33871c64-0c22-45ce-8aa4-0ec237ac4a31', uuid: generateAci(),
}), }),
}, },
], ],

View file

@ -5,13 +5,14 @@ import * as React from 'react';
import { times } from 'lodash'; import { times } from 'lodash';
import { boolean } from '@storybook/addon-knobs'; import { boolean } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { v4 as generateUuid } from 'uuid';
import { AvatarColors } from '../types/Colors'; import { AvatarColors } from '../types/Colors';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import type { PropsType } from './CallingLobby'; import type { PropsType } from './CallingLobby';
import { CallingLobby } from './CallingLobby'; import { CallingLobby } from './CallingLobby';
import { setupI18n } from '../util/setupI18n'; import { setupI18n } from '../util/setupI18n';
import { UUID } from '../types/UUID'; import { generateAci } from '../types/ServiceId';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import { import {
getDefaultConversation, getDefaultConversation,
@ -65,8 +66,8 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
overrideProps.me || overrideProps.me ||
getDefaultConversation({ getDefaultConversation({
color: AvatarColors[0], color: AvatarColors[0],
id: UUID.generate().toString(), id: generateUuid(),
uuid: UUID.generate().toString(), uuid: generateAci(),
}), }),
onCallCanceled: action('on-call-canceled'), onCallCanceled: action('on-call-canceled'),
onJoinCall: action('on-join-call'), onJoinCall: action('on-join-call'),
@ -116,8 +117,8 @@ export function NoCameraLocalAvatar(): JSX.Element {
me: getDefaultConversation({ me: getDefaultConversation({
avatarPath: '/fixtures/kitten-4-112-112.jpg', avatarPath: '/fixtures/kitten-4-112-112.jpg',
color: AvatarColors[0], color: AvatarColors[0],
id: UUID.generate().toString(), id: generateUuid(),
uuid: UUID.generate().toString(), uuid: generateAci(),
}), }),
}); });
return <CallingLobby {...props} />; return <CallingLobby {...props} />;
@ -167,11 +168,11 @@ GroupCall1PeekedParticipant.story = {
}; };
export function GroupCall1PeekedParticipantSelf(): JSX.Element { export function GroupCall1PeekedParticipantSelf(): JSX.Element {
const uuid = UUID.generate().toString(); const uuid = generateAci();
const props = createProps({ const props = createProps({
isGroupCall: true, isGroupCall: true,
me: getDefaultConversation({ me: getDefaultConversation({
id: UUID.generate().toString(), id: generateUuid(),
uuid, uuid,
}), }),
peekedParticipants: [fakePeekedParticipant({ title: 'Ash', uuid })], peekedParticipants: [fakePeekedParticipant({ title: 'Ash', uuid })],

View file

@ -11,6 +11,7 @@ import { getDefaultConversation } from '../test-both/helpers/getDefaultConversat
import type { Props } from './CompositionInput'; import type { Props } from './CompositionInput';
import { CompositionInput } from './CompositionInput'; import { CompositionInput } from './CompositionInput';
import { setupI18n } from '../util/setupI18n'; import { setupI18n } from '../util/setupI18n';
import { generateAci } from '../types/ServiceId';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
@ -137,7 +138,7 @@ export function Mentions(): JSX.Element {
{ {
start: 5, start: 5,
length: 1, length: 1,
mentionUuid: '0', mentionUuid: generateAci(),
conversationID: 'k', conversationID: 'k',
replacementText: 'Kate Beaton', replacementText: 'Kate Beaton',
}, },

View file

@ -26,7 +26,7 @@ import { BodyRange, collapseRangeTree, insertRange } from '../types/BodyRange';
import type { LocalizerType, ThemeType } from '../types/Util'; import type { LocalizerType, ThemeType } from '../types/Util';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import { isValidUuid } from '../types/UUID'; import { isServiceIdString } from '../types/ServiceId';
import { MentionBlot } from '../quill/mentions/blot'; import { MentionBlot } from '../quill/mentions/blot';
import { import {
matchEmojiImage, matchEmojiImage,
@ -46,6 +46,7 @@ import {
import { SignalClipboard } from '../quill/signal-clipboard'; import { SignalClipboard } from '../quill/signal-clipboard';
import { DirectionalBlot } from '../quill/block/blot'; import { DirectionalBlot } from '../quill/block/blot';
import { getClassNamesFor } from '../util/getClassNamesFor'; import { getClassNamesFor } from '../util/getClassNamesFor';
import { isNotNil } from '../util/isNotNil';
import * as log from '../logging/log'; import * as log from '../logging/log';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import { useRefMerger } from '../hooks/useRefMerger'; import { useRefMerger } from '../hooks/useRefMerger';
@ -677,11 +678,15 @@ export function CompositionInput(props: Props): React.ReactElement {
return; return;
} }
const currentMemberUuids = currentMembers const currentMemberServiceIds = currentMembers
.map(m => m.uuid) .map(m => m.uuid)
.filter(isValidUuid); .filter(isNotNil)
.filter(isServiceIdString);
const newDelta = getDeltaToRemoveStaleMentions(ops, currentMemberUuids); const newDelta = getDeltaToRemoveStaleMentions(
ops,
currentMemberServiceIds
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
quill.updateContents(newDelta as any); quill.updateContents(newDelta as any);

View file

@ -3,6 +3,7 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { times, omit } from 'lodash'; import { times, omit } from 'lodash';
import { v4 as generateUuid } from 'uuid';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { boolean, date, select, text } from '@storybook/addon-knobs'; import { boolean, date, select, text } from '@storybook/addon-knobs';
@ -18,8 +19,7 @@ import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import { ThemeType } from '../types/Util'; import { ThemeType } from '../types/Util';
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
import { UUID } from '../types/UUID'; import { makeFakeLookupConversationWithoutServiceId } from '../test-both/helpers/fakeLookupConversationWithoutServiceId';
import { makeFakeLookupConversationWithoutUuid } from '../test-both/helpers/fakeLookupConversationWithoutUuid';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -95,7 +95,7 @@ function Wrapper({
/> />
)} )}
scrollable={scrollable} scrollable={scrollable}
lookupConversationWithoutUuid={makeFakeLookupConversationWithoutUuid()} lookupConversationWithoutServiceId={makeFakeLookupConversationWithoutServiceId()}
showChooseGroupMembers={action('showChooseGroupMembers')} showChooseGroupMembers={action('showChooseGroupMembers')}
showUserNotFoundModal={action('showUserNotFoundModal')} showUserNotFoundModal={action('showUserNotFoundModal')}
setIsFetchingUUID={action('setIsFetchingUUID')} setIsFetchingUUID={action('setIsFetchingUUID')}
@ -381,7 +381,7 @@ ConversationsMessageStatuses.story = {
export const ConversationTypingStatus = (): JSX.Element => export const ConversationTypingStatus = (): JSX.Element =>
renderConversation({ renderConversation({
typingContactId: UUID.generate().toString(), typingContactId: generateUuid(),
}); });
ConversationTypingStatus.story = { ConversationTypingStatus.story = {

View file

@ -14,7 +14,7 @@ import type { LocalizerType, ThemeType } from '../types/Util';
import { ScrollBehavior } from '../types/Util'; import { ScrollBehavior } from '../types/Util';
import { getNavSidebarWidthBreakpoint } from './_util'; import { getNavSidebarWidthBreakpoint } from './_util';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import type { LookupConversationWithoutUuidActionsType } from '../util/lookupConversationWithoutUuid'; import type { LookupConversationWithoutServiceIdActionsType } from '../util/lookupConversationWithoutServiceId';
import type { ShowConversationType } from '../state/ducks/conversations'; import type { ShowConversationType } from '../state/ducks/conversations';
import type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem'; import type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem';
@ -189,7 +189,7 @@ export type PropsType = {
renderMessageSearchResult?: (id: string) => JSX.Element; renderMessageSearchResult?: (id: string) => JSX.Element;
showChooseGroupMembers: () => void; showChooseGroupMembers: () => void;
showConversation: ShowConversationType; showConversation: ShowConversationType;
} & LookupConversationWithoutUuidActionsType; } & LookupConversationWithoutServiceIdActionsType;
const NORMAL_ROW_HEIGHT = 76; const NORMAL_ROW_HEIGHT = 76;
const SELECT_ROW_HEIGHT = 52; const SELECT_ROW_HEIGHT = 52;
@ -214,7 +214,7 @@ export function ConversationList({
scrollable = true, scrollable = true,
shouldRecomputeRowHeights, shouldRecomputeRowHeights,
showChooseGroupMembers, showChooseGroupMembers,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
showConversation, showConversation,
@ -313,7 +313,9 @@ export function ConversationList({
result = ( result = (
<PhoneNumberCheckboxComponent <PhoneNumberCheckboxComponent
phoneNumber={row.phoneNumber} phoneNumber={row.phoneNumber}
lookupConversationWithoutUuid={lookupConversationWithoutUuid} lookupConversationWithoutServiceId={
lookupConversationWithoutServiceId
}
showUserNotFoundModal={showUserNotFoundModal} showUserNotFoundModal={showUserNotFoundModal}
setIsFetchingUUID={setIsFetchingUUID} setIsFetchingUUID={setIsFetchingUUID}
toggleConversationInChooseMembers={conversationId => toggleConversationInChooseMembers={conversationId =>
@ -330,7 +332,9 @@ export function ConversationList({
result = ( result = (
<UsernameCheckboxComponent <UsernameCheckboxComponent
username={row.username} username={row.username}
lookupConversationWithoutUuid={lookupConversationWithoutUuid} lookupConversationWithoutServiceId={
lookupConversationWithoutServiceId
}
showUserNotFoundModal={showUserNotFoundModal} showUserNotFoundModal={showUserNotFoundModal}
setIsFetchingUUID={setIsFetchingUUID} setIsFetchingUUID={setIsFetchingUUID}
toggleConversationInChooseMembers={conversationId => toggleConversationInChooseMembers={conversationId =>
@ -436,7 +440,9 @@ export function ConversationList({
i18n={i18n} i18n={i18n}
phoneNumber={row.phoneNumber} phoneNumber={row.phoneNumber}
isFetching={row.isFetching} isFetching={row.isFetching}
lookupConversationWithoutUuid={lookupConversationWithoutUuid} lookupConversationWithoutServiceId={
lookupConversationWithoutServiceId
}
showUserNotFoundModal={showUserNotFoundModal} showUserNotFoundModal={showUserNotFoundModal}
setIsFetchingUUID={setIsFetchingUUID} setIsFetchingUUID={setIsFetchingUUID}
showConversation={showConversation} showConversation={showConversation}
@ -449,7 +455,9 @@ export function ConversationList({
i18n={i18n} i18n={i18n}
username={row.username} username={row.username}
isFetchingUsername={row.isFetchingUsername} isFetchingUsername={row.isFetchingUsername}
lookupConversationWithoutUuid={lookupConversationWithoutUuid} lookupConversationWithoutServiceId={
lookupConversationWithoutServiceId
}
showUserNotFoundModal={showUserNotFoundModal} showUserNotFoundModal={showUserNotFoundModal}
setIsFetchingUUID={setIsFetchingUUID} setIsFetchingUUID={setIsFetchingUUID}
showConversation={showConversation} showConversation={showConversation}
@ -473,7 +481,7 @@ export function ConversationList({
getPreferredBadge, getPreferredBadge,
getRow, getRow,
i18n, i18n,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
onClickArchiveButton, onClickArchiveButton,
onClickContactCheckbox, onClickContactCheckbox,
onOutgoingAudioCallInConversation, onOutgoingAudioCallInConversation,

View file

@ -358,7 +358,9 @@ export function ForwardMessagesModal({
toggleSelectedConversation(conversationId); toggleSelectedConversation(conversationId);
} }
}} }}
lookupConversationWithoutUuid={asyncShouldNeverBeCalled} lookupConversationWithoutServiceId={
asyncShouldNeverBeCalled
}
showConversation={shouldNeverBeCalled} showConversation={shouldNeverBeCalled}
showUserNotFoundModal={shouldNeverBeCalled} showUserNotFoundModal={shouldNeverBeCalled}
setIsFetchingUUID={shouldNeverBeCalled} setIsFetchingUUID={shouldNeverBeCalled}

View file

@ -10,6 +10,7 @@ import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { FRAME_BUFFER_SIZE } from '../calling/constants'; import { FRAME_BUFFER_SIZE } from '../calling/constants';
import { setupI18n } from '../util/setupI18n'; import { setupI18n } from '../util/setupI18n';
import { generateAci } from '../types/ServiceId';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -60,7 +61,7 @@ const createProps = (
isBlocked: Boolean(isBlocked), isBlocked: Boolean(isBlocked),
title: title:
'Pablo Diego José Francisco de Paula Juan Nepomuceno María de los Remedios Cipriano de la Santísima Trinidad Ruiz y Picasso', 'Pablo Diego José Francisco de Paula Juan Nepomuceno María de los Remedios Cipriano de la Santísima Trinidad Ruiz y Picasso',
uuid: '992ed3b9-fc9b-47a9-bdb4-e0c7cbb0fda5', uuid: generateAci(),
}), }),
}, },
remoteParticipantsCount: 1, remoteParticipantsCount: 1,

View file

@ -31,9 +31,9 @@ import { DialogType } from '../types/Dialogs';
import { SocketStatus } from '../types/SocketStatus'; import { SocketStatus } from '../types/SocketStatus';
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
import { import {
makeFakeLookupConversationWithoutUuid, makeFakeLookupConversationWithoutServiceId,
useUuidFetchState, useUuidFetchState,
} from '../test-both/helpers/fakeLookupConversationWithoutUuid'; } from '../test-both/helpers/fakeLookupConversationWithoutServiceId';
import type { GroupListItemConversationType } from './conversationList/GroupListItem'; import type { GroupListItemConversationType } from './conversationList/GroupListItem';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -168,7 +168,8 @@ const useProps = (overrideProps: OverridePropsType = {}): PropsType => {
navTabsCollapsed: boolean('navTabsCollapsed', false), navTabsCollapsed: boolean('navTabsCollapsed', false),
setChallengeStatus: action('setChallengeStatus'), setChallengeStatus: action('setChallengeStatus'),
lookupConversationWithoutUuid: makeFakeLookupConversationWithoutUuid(), lookupConversationWithoutServiceId:
makeFakeLookupConversationWithoutServiceId(),
showUserNotFoundModal: action('showUserNotFoundModal'), showUserNotFoundModal: action('showUserNotFoundModal'),
setIsFetchingUUID, setIsFetchingUUID,
showConversation: action('showConversation'), showConversation: action('showConversation'),

View file

@ -29,7 +29,7 @@ import type { DurationInSeconds } from '../util/durations';
import type { WidthBreakpoint } from './_util'; import type { WidthBreakpoint } from './_util';
import { getNavSidebarWidthBreakpoint } from './_util'; import { getNavSidebarWidthBreakpoint } from './_util';
import * as KeyboardLayout from '../services/keyboardLayout'; import * as KeyboardLayout from '../services/keyboardLayout';
import type { LookupConversationWithoutUuidActionsType } from '../util/lookupConversationWithoutUuid'; import type { LookupConversationWithoutServiceIdActionsType } from '../util/lookupConversationWithoutServiceId';
import type { ShowConversationType } from '../state/ducks/conversations'; import type { ShowConversationType } from '../state/ducks/conversations';
import type { PropsType as UnsupportedOSDialogPropsType } from '../state/smart/UnsupportedOSDialog'; import type { PropsType as UnsupportedOSDialogPropsType } from '../state/smart/UnsupportedOSDialog';
@ -152,7 +152,7 @@ export type PropsType = {
renderCaptchaDialog: (props: { onSkip(): void }) => JSX.Element; renderCaptchaDialog: (props: { onSkip(): void }) => JSX.Element;
renderCrashReportDialog: () => JSX.Element; renderCrashReportDialog: () => JSX.Element;
renderExpiredBuildDialog: (_: DialogExpiredBuildPropsType) => JSX.Element; renderExpiredBuildDialog: (_: DialogExpiredBuildPropsType) => JSX.Element;
} & LookupConversationWithoutUuidActionsType; } & LookupConversationWithoutServiceIdActionsType;
export function LeftPane({ export function LeftPane({
blockConversation, blockConversation,
@ -173,7 +173,7 @@ export function LeftPane({
hasRelinkDialog, hasRelinkDialog,
hasUpdateDialog, hasUpdateDialog,
i18n, i18n,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
isMacOS, isMacOS,
isUpdateDownloaded, isUpdateDownloaded,
isContactManagementEnabled, isContactManagementEnabled,
@ -657,8 +657,8 @@ export function LeftPane({
}} }}
showUserNotFoundModal={showUserNotFoundModal} showUserNotFoundModal={showUserNotFoundModal}
setIsFetchingUUID={setIsFetchingUUID} setIsFetchingUUID={setIsFetchingUUID}
lookupConversationWithoutUuid={ lookupConversationWithoutServiceId={
lookupConversationWithoutUuid lookupConversationWithoutServiceId
} }
showConversation={showConversation} showConversation={showConversation}
blockConversation={blockConversation} blockConversation={blockConversation}

View file

@ -5,6 +5,7 @@ import type { Meta, Story } from '@storybook/react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import React, { useState } from 'react'; import React, { useState } from 'react';
import casual from 'casual'; import casual from 'casual';
import { v4 as generateUuid } from 'uuid';
import type { PropsType } from './ProfileEditor'; import type { PropsType } from './ProfileEditor';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
@ -15,7 +16,6 @@ import {
UsernameLinkState, UsernameLinkState,
UsernameReservationState, UsernameReservationState,
} from '../state/ducks/usernameEnums'; } from '../state/ducks/usernameEnums';
import { UUID } from '../types/UUID';
import { getRandomColor } from '../test-both/helpers/getRandomColor'; import { getRandomColor } from '../test-both/helpers/getRandomColor';
import { setupI18n } from '../util/setupI18n'; import { setupI18n } from '../util/setupI18n';
@ -35,7 +35,7 @@ export default {
defaultValue: undefined, defaultValue: undefined,
}, },
conversationId: { conversationId: {
defaultValue: UUID.generate().toString(), defaultValue: generateUuid(),
}, },
color: { color: {
defaultValue: getRandomColor(), defaultValue: getRandomColor(),

View file

@ -11,6 +11,7 @@ import enMessages from '../../_locales/en/messages.json';
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
import { getFakeBadge } from '../test-both/helpers/getFakeBadge'; import { getFakeBadge } from '../test-both/helpers/getFakeBadge';
import { MY_STORY_ID } from '../types/Stories'; import { MY_STORY_ID } from '../types/Stories';
import { generateStoryDistributionId } from '../types/StoryDistributionId';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -254,7 +255,7 @@ export function NoContacts(): JSX.Element {
story: { story: {
name: 'Custom List A', name: 'Custom List A',
conversationId: 'our-conversation-id', conversationId: 'our-conversation-id',
distributionId: 'some-other-distribution-id', distributionId: generateStoryDistributionId(),
}, },
contacts: [], contacts: [],
}, },
@ -290,7 +291,7 @@ export function InMultipleStories(): JSX.Element {
story: { story: {
name: 'Custom List A', name: 'Custom List A',
conversationId: 'our-conversation-id', conversationId: 'our-conversation-id',
distributionId: 'some-other-distribution-id', distributionId: generateStoryDistributionId(),
}, },
contacts: [ contacts: [
contactWithAllData, contactWithAllData,

View file

@ -21,7 +21,8 @@ import { ContextMenu } from './ContextMenu';
import { Theme } from '../util/theme'; import { Theme } from '../util/theme';
import { isNotNil } from '../util/isNotNil'; import { isNotNil } from '../util/isNotNil';
import { MY_STORY_ID } from '../types/Stories'; import { MY_STORY_ID } from '../types/Stories';
import type { UUIDStringType } from '../types/UUID'; import type { ServiceIdString } from '../types/ServiceId';
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import { UserText } from './UserText'; import { UserText } from './UserText';
export enum SafetyNumberChangeSource { export enum SafetyNumberChangeSource {
@ -48,7 +49,7 @@ type StoryContacts = {
// For My Story or custom distribution lists, conversationId will be our own // For My Story or custom distribution lists, conversationId will be our own
conversationId: string; conversationId: string;
// For Group stories, distributionId will not be provided // For Group stories, distributionId will not be provided
distributionId?: string; distributionId?: StoryDistributionIdString;
}; };
contacts: Array<ConversationType>; contacts: Array<ConversationType>;
}; };
@ -62,8 +63,8 @@ export type Props = Readonly<{
onCancel: () => void; onCancel: () => void;
onConfirm: () => void; onConfirm: () => void;
removeFromStory?: ( removeFromStory?: (
distributionId: string, distributionId: StoryDistributionIdString,
uuids: Array<UUIDStringType> serviceIds: Array<ServiceIdString>
) => unknown; ) => unknown;
renderSafetyNumber: (props: SafetyNumberProps) => JSX.Element; renderSafetyNumber: (props: SafetyNumberProps) => JSX.Element;
theme: ThemeType; theme: ThemeType;
@ -275,8 +276,8 @@ function ContactSection({
getPreferredBadge: PreferredBadgeSelectorType; getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType; i18n: LocalizerType;
removeFromStory?: ( removeFromStory?: (
distributionId: string, distributionId: StoryDistributionIdString,
uuids: Array<UUIDStringType> serviceIds: Array<ServiceIdString>
) => unknown; ) => unknown;
setSelectedContact: (contact: ConversationType) => void; setSelectedContact: (contact: ConversationType) => void;
theme: ThemeType; theme: ThemeType;
@ -431,12 +432,12 @@ function ContactRow({
theme, theme,
}: Readonly<{ }: Readonly<{
contact: ConversationType; contact: ConversationType;
distributionId?: string; distributionId?: StoryDistributionIdString;
getPreferredBadge: PreferredBadgeSelectorType; getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType; i18n: LocalizerType;
removeFromStory?: ( removeFromStory?: (
distributionId: string, distributionId: StoryDistributionIdString,
uuids: Array<UUIDStringType> serviceIds: Array<ServiceIdString>
) => unknown; ) => unknown;
setSelectedContact: (contact: ConversationType) => void; setSelectedContact: (contact: ConversationType) => void;
shouldShowNumber: boolean; shouldShowNumber: boolean;

View file

@ -21,7 +21,8 @@ import {
Page as StoriesSettingsPage, Page as StoriesSettingsPage,
} from './StoriesSettingsModal'; } from './StoriesSettingsModal';
import type { StoryDistributionListWithMembersDataType } from '../types/Stories'; import type { StoryDistributionListWithMembersDataType } from '../types/Stories';
import type { UUIDStringType } from '../types/UUID'; import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import type { ServiceIdString } from '../types/ServiceId';
import { Alert } from './Alert'; import { Alert } from './Alert';
import { Avatar, AvatarSize } from './Avatar'; import { Avatar, AvatarSize } from './Avatar';
import { Button, ButtonSize, ButtonVariant } from './Button'; import { Button, ButtonSize, ButtonVariant } from './Button';
@ -54,18 +55,18 @@ export type PropsType = {
i18n: LocalizerType; i18n: LocalizerType;
me: ConversationType; me: ConversationType;
onClose: () => unknown; onClose: () => unknown;
onDeleteList: (listId: string) => unknown; onDeleteList: (listId: StoryDistributionIdString) => unknown;
onDistributionListCreated: ( onDistributionListCreated: (
name: string, name: string,
viewerUuids: Array<UUIDStringType> viewerUuids: Array<ServiceIdString>
) => Promise<UUIDStringType>; ) => Promise<StoryDistributionIdString>;
onSelectedStoryList: (options: { onSelectedStoryList: (options: {
conversationId: string; conversationId: string;
distributionId: string | undefined; distributionId: StoryDistributionIdString | undefined;
uuids: Array<UUIDStringType>; serviceIds: Array<ServiceIdString>;
}) => unknown; }) => unknown;
onSend: ( onSend: (
listIds: Array<UUIDStringType>, listIds: Array<StoryDistributionIdString>,
conversationIds: Array<string> conversationIds: Array<string>
) => unknown; ) => unknown;
signalConnections: Array<ConversationType>; signalConnections: Array<ConversationType>;
@ -99,21 +100,23 @@ const Page = {
type PageType = SendStoryPage | StoriesSettingsPage; type PageType = SendStoryPage | StoriesSettingsPage;
function getListMemberUuids( function getListMemberServiceIds(
list: StoryDistributionListWithMembersDataType, list: StoryDistributionListWithMembersDataType,
signalConnections: Array<ConversationType> signalConnections: Array<ConversationType>
): Array<UUIDStringType> { ): Array<ServiceIdString> {
const memberUuids = list.members.map(({ uuid }) => uuid).filter(isNotNil); const memberServiceIds = list.members
.map(({ uuid }) => uuid)
.filter(isNotNil);
if (list.id === MY_STORY_ID && list.isBlockList) { if (list.id === MY_STORY_ID && list.isBlockList) {
const excludeUuids = new Set<string>(memberUuids); const excludeUuids = new Set<string>(memberServiceIds);
return signalConnections return signalConnections
.map(conversation => conversation.uuid) .map(conversation => conversation.uuid)
.filter(isNotNil) .filter(isNotNil)
.filter(uuid => !excludeUuids.has(uuid)); .filter(uuid => !excludeUuids.has(uuid));
} }
return memberUuids; return memberServiceIds;
} }
export function SendStoryModal({ export function SendStoryModal({
@ -147,9 +150,9 @@ export function SendStoryModal({
const [confirmDiscardModal, confirmDiscardIf] = useConfirmDiscard(i18n); const [confirmDiscardModal, confirmDiscardIf] = useConfirmDiscard(i18n);
const [selectedListIds, setSelectedListIds] = useState<Set<UUIDStringType>>( const [selectedListIds, setSelectedListIds] = useState<
new Set() Set<StoryDistributionIdString>
); >(new Set());
const [selectedGroupIds, setSelectedGroupIds] = useState<Set<string>>( const [selectedGroupIds, setSelectedGroupIds] = useState<Set<string>>(
new Set() new Set()
); );
@ -215,7 +218,7 @@ export function SendStoryModal({
string | undefined string | undefined
>(); >();
const [confirmDeleteList, setConfirmDeleteList] = useState< const [confirmDeleteList, setConfirmDeleteList] = useState<
{ id: string; name: string } | undefined { id: StoryDistributionIdString; name: string } | undefined
>(); >();
const [listIdToEdit, setListIdToEdit] = useState<string | undefined>(); const [listIdToEdit, setListIdToEdit] = useState<string | undefined>();
@ -263,7 +266,7 @@ export function SendStoryModal({
selectedNames = chosenGroupNames.join(', '); selectedNames = chosenGroupNames.join(', ');
} else { } else {
selectedNames = selectedStoryNames selectedNames = selectedStoryNames
.map(listName => getStoryDistributionListName(i18n, listName, listName)) .map(listName => getStoryDistributionListName(i18n, undefined, listName))
.join(', '); .join(', ');
} }
@ -661,7 +664,7 @@ export function SendStoryModal({
onSelectedStoryList({ onSelectedStoryList({
conversationId: ourConversationId, conversationId: ourConversationId,
distributionId: list.id, distributionId: list.id,
uuids: getListMemberUuids(list, signalConnections), serviceIds: getListMemberServiceIds(list, signalConnections),
}); });
} }
}} }}
@ -792,7 +795,7 @@ export function SendStoryModal({
onSelectedStoryList({ onSelectedStoryList({
conversationId: group.id, conversationId: group.id,
distributionId: undefined, distributionId: undefined,
uuids: group.memberships.map(({ uuid }) => uuid), serviceIds: group.memberships.map(({ uuid }) => uuid),
}); });
} }
}} }}

View file

@ -115,7 +115,9 @@ export const SingleList = Template.bind({});
}, },
{ {
...fakeDistroList, ...fakeDistroList,
members: fakeDistroList.memberUuids.map(() => getDefaultConversation()), members: fakeDistroList.memberServiceIds.map(() =>
getDefaultConversation()
),
}, },
], ],
}; };

View file

@ -11,7 +11,8 @@ import type { LocalizerType } from '../types/Util';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import type { Row } from './ConversationList'; import type { Row } from './ConversationList';
import type { StoryDistributionListWithMembersDataType } from '../types/Stories'; import type { StoryDistributionListWithMembersDataType } from '../types/Stories';
import type { UUIDStringType } from '../types/UUID'; import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import type { ServiceIdString } from '../types/ServiceId';
import type { RenderModalPage, ModalPropsType } from './Modal'; import type { RenderModalPage, ModalPropsType } from './Modal';
import { Avatar, AvatarSize } from './Avatar'; import { Avatar, AvatarSize } from './Avatar';
import { Button, ButtonVariant } from './Button'; import { Button, ButtonVariant } from './Button';
@ -28,7 +29,6 @@ import { SearchInput } from './SearchInput';
import { StoryDistributionListName } from './StoryDistributionListName'; import { StoryDistributionListName } from './StoryDistributionListName';
import { Theme } from '../util/theme'; import { Theme } from '../util/theme';
import { ThemeType } from '../types/Util'; import { ThemeType } from '../types/Util';
import { UUID } from '../types/UUID';
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations'; import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
import { isNotNil } from '../util/isNotNil'; import { isNotNil } from '../util/isNotNil';
import { import {
@ -54,23 +54,25 @@ export type PropsType = {
toggleGroupsForStorySend: (groupIds: Array<string>) => unknown; toggleGroupsForStorySend: (groupIds: Array<string>) => unknown;
onDistributionListCreated: ( onDistributionListCreated: (
name: string, name: string,
viewerUuids: Array<UUIDStringType> viewerUuids: Array<ServiceIdString>
) => Promise<string>; ) => Promise<string>;
onHideMyStoriesFrom: (viewerUuids: Array<UUIDStringType>) => unknown; onHideMyStoriesFrom: (viewerUuids: Array<ServiceIdString>) => unknown;
onRemoveMembers: (listId: string, uuids: Array<UUIDStringType>) => unknown; onRemoveMembers: (listId: string, uuids: Array<ServiceIdString>) => unknown;
onRepliesNReactionsChanged: ( onRepliesNReactionsChanged: (
listId: string, listId: string,
allowsReplies: boolean allowsReplies: boolean
) => unknown; ) => unknown;
onViewersUpdated: ( onViewersUpdated: (
listId: string, listId: string,
viewerUuids: Array<UUIDStringType> viewerUuids: Array<ServiceIdString>
) => unknown; ) => unknown;
setMyStoriesToAllSignalConnections: () => unknown; setMyStoriesToAllSignalConnections: () => unknown;
storyViewReceiptsEnabled: boolean; storyViewReceiptsEnabled: boolean;
toggleSignalConnectionsModal: () => unknown; toggleSignalConnectionsModal: () => unknown;
setStoriesDisabled: (value: boolean) => void; setStoriesDisabled: (value: boolean) => void;
getConversationByUuid: (uuid: UUIDStringType) => ConversationType | undefined; getConversationByUuid: (
uuid: ServiceIdString
) => ConversationType | undefined;
}; };
export enum Page { export enum Page {
@ -134,7 +136,7 @@ type DistributionListItemProps = {
distributionList: StoryDistributionListWithMembersDataType; distributionList: StoryDistributionListWithMembersDataType;
me: ConversationType; me: ConversationType;
signalConnections: Array<ConversationType>; signalConnections: Array<ConversationType>;
onSelectItemToEdit(id: UUIDStringType): void; onSelectItemToEdit(id: StoryDistributionIdString): void;
}; };
function DistributionListItem({ function DistributionListItem({
@ -543,7 +545,10 @@ type DistributionListSettingsModalPropsType = {
i18n: LocalizerType; i18n: LocalizerType;
listToEdit: StoryDistributionListWithMembersDataType; listToEdit: StoryDistributionListWithMembersDataType;
signalConnectionsCount: number; signalConnectionsCount: number;
setConfirmDeleteList: (_: { id: string; name: string }) => unknown; setConfirmDeleteList: (_: {
id: StoryDistributionIdString;
name: string;
}) => unknown;
setPage: (page: Page) => unknown; setPage: (page: Page) => unknown;
setSelectedContacts: (contacts: Array<ConversationType>) => unknown; setSelectedContacts: (contacts: Array<ConversationType>) => unknown;
onBackButtonClick: (() => void) | undefined; onBackButtonClick: (() => void) | undefined;
@ -577,7 +582,7 @@ export function DistributionListSettingsModal({
| { | {
listId: string; listId: string;
title: string; title: string;
uuid: UUIDStringType; uuid: ServiceIdString;
} }
>(); >();
@ -945,8 +950,8 @@ export function EditMyStoryPrivacy({
} }
type EditDistributionListModalPropsType = { type EditDistributionListModalPropsType = {
onCreateList: (name: string, viewerUuids: Array<UUIDStringType>) => unknown; onCreateList: (name: string, viewerUuids: Array<ServiceIdString>) => unknown;
onViewersUpdated: (viewerUuids: Array<UUIDStringType>) => unknown; onViewersUpdated: (viewerUuids: Array<ServiceIdString>) => unknown;
page: page:
| Page.AddViewer | Page.AddViewer
| Page.ChooseViewers | Page.ChooseViewers
@ -998,7 +1003,7 @@ export function EditDistributionListModal({
return map; return map;
}, [candidateConversations]); }, [candidateConversations]);
const selectedConversationUuids: Set<UUIDStringType> = useMemo( const selectConversationServiceIds: Set<ServiceIdString> = useMemo(
() => () =>
new Set(selectedContacts.map(contact => contact.uuid).filter(isNotNil)), new Set(selectedContacts.map(contact => contact.uuid).filter(isNotNil)),
[selectedContacts] [selectedContacts]
@ -1034,7 +1039,7 @@ export function EditDistributionListModal({
<Button <Button
disabled={!storyName} disabled={!storyName}
onClick={() => { onClick={() => {
onCreateList(storyName, Array.from(selectedConversationUuids)); onCreateList(storyName, Array.from(selectConversationServiceIds));
setStoryName(''); setStoryName('');
}} }}
variant={ButtonVariant.Primary} variant={ButtonVariant.Primary}
@ -1105,7 +1110,7 @@ export function EditDistributionListModal({
return undefined; return undefined;
} }
const isSelected = selectedConversationUuids.has(UUID.cast(contact.uuid)); const isSelected = selectConversationServiceIds.has(contact.uuid);
return { return {
type: RowType.ContactCheckbox, type: RowType.ContactCheckbox,
@ -1120,7 +1125,7 @@ export function EditDistributionListModal({
<Button <Button
disabled={selectedContacts.length === 0} disabled={selectedContacts.length === 0}
onClick={() => { onClick={() => {
onViewersUpdated(Array.from(selectedConversationUuids)); onViewersUpdated(Array.from(selectConversationServiceIds));
}} }}
variant={ButtonVariant.Primary} variant={ButtonVariant.Primary}
> >
@ -1132,7 +1137,7 @@ export function EditDistributionListModal({
<Button <Button
disabled={selectedContacts.length === 0} disabled={selectedContacts.length === 0}
onClick={() => { onClick={() => {
onViewersUpdated(Array.from(selectedConversationUuids)); onViewersUpdated(Array.from(selectConversationServiceIds));
}} }}
variant={ButtonVariant.Primary} variant={ButtonVariant.Primary}
> >
@ -1197,7 +1202,7 @@ export function EditDistributionListModal({
getPreferredBadge={getPreferredBadge} getPreferredBadge={getPreferredBadge}
getRow={getRow} getRow={getRow}
i18n={i18n} i18n={i18n}
lookupConversationWithoutUuid={asyncShouldNeverBeCalled} lookupConversationWithoutServiceId={asyncShouldNeverBeCalled}
onClickArchiveButton={shouldNeverBeCalled} onClickArchiveButton={shouldNeverBeCalled}
onClickContactCheckbox={(conversationId: string) => { onClickContactCheckbox={(conversationId: string) => {
toggleSelectedConversation(conversationId); toggleSelectedConversation(conversationId);
@ -1236,7 +1241,7 @@ type GroupStorySettingsModalProps = {
group: ConversationType; group: ConversationType;
onClose(): void; onClose(): void;
onBackButtonClick(): void; onBackButtonClick(): void;
getConversationByUuid(uuid: UUIDStringType): ConversationType | undefined; getConversationByUuid(uuid: ServiceIdString): ConversationType | undefined;
onRemoveGroup(group: ConversationType): void; onRemoveGroup(group: ConversationType): void;
}; };

View file

@ -14,7 +14,7 @@ import type { LinkPreviewType } from '../types/message/LinkPreviews';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
import type { Props as StickerButtonProps } from './stickers/StickerButton'; import type { Props as StickerButtonProps } from './stickers/StickerButton';
import type { PropsType as SendStoryModalPropsType } from './SendStoryModal'; import type { PropsType as SendStoryModalPropsType } from './SendStoryModal';
import type { UUIDStringType } from '../types/UUID'; import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import type { imageToBlurHash } from '../util/imageToBlurHash'; import type { imageToBlurHash } from '../util/imageToBlurHash';
import type { PropsType as TextStoryCreatorPropsType } from './TextStoryCreator'; import type { PropsType as TextStoryCreatorPropsType } from './TextStoryCreator';
@ -54,7 +54,7 @@ export type PropsType = {
linkPreview?: LinkPreviewType; linkPreview?: LinkPreviewType;
onClose: () => unknown; onClose: () => unknown;
onSend: ( onSend: (
listIds: Array<UUIDStringType>, listIds: Array<StoryDistributionIdString>,
conversationIds: Array<string>, conversationIds: Array<string>,
attachment: AttachmentType, attachment: AttachmentType,
bodyRanges: DraftBodyRanges | undefined bodyRanges: DraftBodyRanges | undefined

View file

@ -5,11 +5,12 @@ import React from 'react';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
import { getStoryDistributionListName } from '../types/Stories'; import { getStoryDistributionListName } from '../types/Stories';
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import { UserText } from './UserText'; import { UserText } from './UserText';
type PropsType = { type PropsType = {
i18n: LocalizerType; i18n: LocalizerType;
id: string; id: StoryDistributionIdString | string;
name: string; name: string;
}; };

View file

@ -8,6 +8,7 @@ import type { PropsType } from './StoryViewer';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import { SendStatus } from '../messages/MessageSendState'; import { SendStatus } from '../messages/MessageSendState';
import { StoryViewModeType } from '../types/Stories'; import { StoryViewModeType } from '../types/Stories';
import { generateStoryDistributionId } from '../types/StoryDistributionId';
import { StoryViewer } from './StoryViewer'; import { StoryViewer } from './StoryViewer';
import { VIDEO_MP4 } from '../types/MIME'; import { VIDEO_MP4 } from '../types/MIME';
import { fakeAttachment } from '../test-both/helpers/fakeAttachment'; import { fakeAttachment } from '../test-both/helpers/fakeAttachment';
@ -170,7 +171,10 @@ export const YourStory = Template.bind({});
); );
YourStory.args = { YourStory.args = {
distributionList: { id: '123', name: 'Close Friends' }, distributionList: {
id: generateStoryDistributionId(),
name: 'Close Friends',
},
story: { story: {
...storyView, ...storyView,
sender: { sender: {
@ -203,7 +207,10 @@ export const YourStoryFailed = Template.bind({});
); );
YourStoryFailed.args = { YourStoryFailed.args = {
distributionList: { id: '123', name: 'Close Friends' }, distributionList: {
id: generateStoryDistributionId(),
name: 'Close Friends',
},
story: { story: {
...storyView, ...storyView,
sender: { sender: {

View file

@ -21,6 +21,7 @@ import type { EmojiPickDataType } from './emoji/EmojiPicker';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import type { RenderEmojiPickerProps } from './conversation/ReactionPicker'; import type { RenderEmojiPickerProps } from './conversation/ReactionPicker';
import type { ReplyStateType, StoryViewType } from '../types/Stories'; import type { ReplyStateType, StoryViewType } from '../types/Stories';
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import type { ShowToastAction } from '../state/ducks/toast'; import type { ShowToastAction } from '../state/ducks/toast';
import type { ViewStoryActionCreatorType } from '../state/ducks/stories'; import type { ViewStoryActionCreatorType } from '../state/ducks/stories';
import * as log from '../logging/log'; import * as log from '../logging/log';
@ -66,7 +67,7 @@ export type PropsType = {
deleteGroupStoryReply: (id: string) => void; deleteGroupStoryReply: (id: string) => void;
deleteGroupStoryReplyForEveryone: (id: string) => void; deleteGroupStoryReplyForEveryone: (id: string) => void;
deleteStoryForEveryone: (story: StoryViewType) => unknown; deleteStoryForEveryone: (story: StoryViewType) => unknown;
distributionList?: { id: string; name: string }; distributionList?: { id: StoryDistributionIdString; name: string };
getPreferredBadge: PreferredBadgeSelectorType; getPreferredBadge: PreferredBadgeSelectorType;
group?: Pick< group?: Pick<
ConversationType, ConversationType,

View file

@ -3,6 +3,7 @@
import type { Meta, Story } from '@storybook/react'; import type { Meta, Story } from '@storybook/react';
import React from 'react'; import React from 'react';
import { v4 as generateUuid } from 'uuid';
import { useArgs } from '@storybook/addons'; import { useArgs } from '@storybook/addons';
import type { PropsType } from './StoryViewsNRepliesModal'; import type { PropsType } from './StoryViewsNRepliesModal';
@ -10,7 +11,6 @@ import * as durations from '../util/durations';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import { SendStatus } from '../messages/MessageSendState'; import { SendStatus } from '../messages/MessageSendState';
import { StoryViewsNRepliesModal } from './StoryViewsNRepliesModal'; import { StoryViewsNRepliesModal } from './StoryViewsNRepliesModal';
import { UUID } from '../types/UUID';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { setupI18n } from '../util/setupI18n'; import { setupI18n } from '../util/setupI18n';
import { StoryViewTargetType } from '../types/Stories'; import { StoryViewTargetType } from '../types/Stories';
@ -110,34 +110,34 @@ function getViewsAndReplies() {
author: p2, author: p2,
body: 'So cute ❤️', body: 'So cute ❤️',
conversationId: p2.id, conversationId: p2.id,
id: UUID.generate().toString(), id: generateUuid(),
timestamp: Date.now() - 24 * durations.MINUTE, timestamp: Date.now() - 24 * durations.MINUTE,
}, },
{ {
author: p3, author: p3,
body: "That's awesome", body: "That's awesome",
conversationId: p3.id, conversationId: p3.id,
id: UUID.generate().toString(), id: generateUuid(),
timestamp: Date.now() - 13 * durations.MINUTE, timestamp: Date.now() - 13 * durations.MINUTE,
}, },
{ {
author: p3, author: p3,
body: 'Very awesome', body: 'Very awesome',
conversationId: p3.id, conversationId: p3.id,
id: UUID.generate().toString(), id: generateUuid(),
timestamp: Date.now() - 13 * durations.MINUTE, timestamp: Date.now() - 13 * durations.MINUTE,
}, },
{ {
author: p3, author: p3,
body: 'Did I mention how awesome this is?', body: 'Did I mention how awesome this is?',
conversationId: p3.id, conversationId: p3.id,
id: UUID.generate().toString(), id: generateUuid(),
timestamp: Date.now() - 12 * durations.MINUTE, timestamp: Date.now() - 12 * durations.MINUTE,
}, },
{ {
author: p4, author: p4,
conversationId: p4.id, conversationId: p4.id,
id: UUID.generate().toString(), id: generateUuid(),
reactionEmoji: '❤️', reactionEmoji: '❤️',
timestamp: Date.now() - 5 * durations.MINUTE, timestamp: Date.now() - 5 * durations.MINUTE,
}, },
@ -145,7 +145,7 @@ function getViewsAndReplies() {
author: p6, author: p6,
body: 'Thanks everyone!', body: 'Thanks everyone!',
conversationId: p6.id, conversationId: p6.id,
id: UUID.generate().toString(), id: generateUuid(),
sendStateByConversationId: { sendStateByConversationId: {
[p1.id]: { [p1.id]: {
status: SendStatus.Pending, status: SendStatus.Pending,

View file

@ -5,9 +5,17 @@ import * as React from 'react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { generateAci } from '../../types/ServiceId';
import type { Props } from './AtMentionify'; import type { Props } from './AtMentionify';
import { AtMentionify } from './AtMentionify'; import { AtMentionify } from './AtMentionify';
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const SERVICE_ID_3 = generateAci();
const SERVICE_ID_4 = generateAci();
const SERVICE_ID_5 = generateAci();
const SERVICE_ID_6 = generateAci();
export default { export default {
title: 'Components/Conversation/AtMentionify', title: 'Components/Conversation/AtMentionify',
}; };
@ -32,21 +40,21 @@ export function MultipleMentions(): JSX.Element {
{ {
start: 4, start: 4,
length: 1, length: 1,
mentionUuid: 'abc', mentionUuid: SERVICE_ID_1,
replacementText: 'Professor Farnsworth', replacementText: 'Professor Farnsworth',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 2, start: 2,
length: 1, length: 1,
mentionUuid: 'def', mentionUuid: SERVICE_ID_2,
replacementText: 'Philip J Fry', replacementText: 'Philip J Fry',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 0, start: 0,
length: 1, length: 1,
mentionUuid: 'xyz', mentionUuid: SERVICE_ID_3,
replacementText: 'Yancy Fry', replacementText: 'Yancy Fry',
conversationID: 'x', conversationID: 'x',
}, },
@ -65,21 +73,21 @@ export function ComplexMentions(): JSX.Element {
{ {
start: 80, start: 80,
length: 1, length: 1,
mentionUuid: 'ioe', mentionUuid: SERVICE_ID_4,
replacementText: 'Cereal Killer', replacementText: 'Cereal Killer',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 78, start: 78,
length: 1, length: 1,
mentionUuid: 'fdr', mentionUuid: SERVICE_ID_5,
replacementText: 'Acid Burn', replacementText: 'Acid Burn',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 4, start: 4,
length: 1, length: 1,
mentionUuid: 'ope', mentionUuid: SERVICE_ID_6,
replacementText: 'Zero Cool', replacementText: 'Zero Cool',
conversationID: 'x', conversationID: 'x',
}, },
@ -101,7 +109,7 @@ export function WithOddCharacter(): JSX.Element {
{ {
start: 4, start: 4,
length: 1, length: 1,
mentionUuid: 'ope', mentionUuid: SERVICE_ID_6,
replacementText: 'Zero Cool', replacementText: 'Zero Cool',
conversationID: 'x', conversationID: 'x',
}, },

View file

@ -7,6 +7,7 @@ import { action } from '@storybook/addon-actions';
import { setupI18n } from '../../util/setupI18n'; import { setupI18n } from '../../util/setupI18n';
import enMessages from '../../../_locales/en/messages.json'; import enMessages from '../../../_locales/en/messages.json';
import { CallMode } from '../../types/Calling'; import { CallMode } from '../../types/Calling';
import { generateAci } from '../../types/ServiceId';
import { CallingNotification, type PropsType } from './CallingNotification'; import { CallingNotification, type PropsType } from './CallingNotification';
import { import {
getDefaultConversation, getDefaultConversation,
@ -19,7 +20,6 @@ import {
GroupCallStatus, GroupCallStatus,
DirectCallStatus, DirectCallStatus,
} from '../../types/CallDisposition'; } from '../../types/CallDisposition';
import { UUID } from '../../types/UUID';
import type { ConversationType } from '../../state/ducks/conversations'; import type { ConversationType } from '../../state/ducks/conversations';
import { CallExternalState } from '../../util/callingNotification'; import { CallExternalState } from '../../util/callingNotification';
@ -45,7 +45,7 @@ const getCommonProps = (options: {
? GroupCallStatus.GenericGroupCall ? GroupCallStatus.GenericGroupCall
: DirectCallStatus.Pending, : DirectCallStatus.Pending,
callCreator = getDefaultConversation({ callCreator = getDefaultConversation({
uuid: UUID.generate().toString(), uuid: generateAci(),
isMe: direction === CallDirection.Outgoing, isMe: direction === CallDirection.Outgoing,
}), }),
callExternalState = CallExternalState.Active, callExternalState = CallExternalState.Active,

View file

@ -5,8 +5,8 @@ import * as React from 'react';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { setupI18n } from '../../util/setupI18n'; import { setupI18n } from '../../util/setupI18n';
import { UUID } from '../../types/UUID'; import { generateAci, generatePni } from '../../types/ServiceId';
import type { UUIDStringType } from '../../types/UUID'; import type { ServiceIdString, AciString } from '../../types/ServiceId';
import enMessages from '../../../_locales/en/messages.json'; import enMessages from '../../../_locales/en/messages.json';
import type { GroupV2ChangeType } from '../../groups'; import type { GroupV2ChangeType } from '../../groups';
import { SignalService as Proto } from '../../protobuf'; import { SignalService as Proto } from '../../protobuf';
@ -16,13 +16,13 @@ import type { FullJSXType } from '../Intl';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
const OUR_ACI = UUID.generate().toString(); const OUR_ACI = generateAci();
const OUR_PNI = UUID.generate().toString(); const OUR_PNI = generatePni();
const CONTACT_A = UUID.generate().toString(); const CONTACT_A = generateAci();
const CONTACT_B = UUID.generate().toString(); const CONTACT_B = generateAci();
const CONTACT_C = UUID.generate().toString(); const CONTACT_C = generateAci();
const ADMIN_A = UUID.generate().toString(); const ADMIN_A = generateAci();
const INVITEE_A = UUID.generate().toString(); const INVITEE_A = generateAci();
const AccessControlEnum = Proto.AccessControl.AccessRequired; const AccessControlEnum = Proto.AccessControl.AccessRequired;
const RoleEnum = Proto.Member.Role; const RoleEnum = Proto.Member.Role;
@ -44,10 +44,10 @@ const renderChange = (
areWeAdmin = true, areWeAdmin = true,
}: { }: {
groupMemberships?: ReadonlyArray<{ groupMemberships?: ReadonlyArray<{
uuid: UUIDStringType; uuid: AciString;
isAdmin: boolean; isAdmin: boolean;
}>; }>;
groupBannedMemberships?: ReadonlyArray<UUIDStringType>; groupBannedMemberships?: ReadonlyArray<ServiceIdString>;
groupName?: string; groupName?: string;
areWeAdmin?: boolean; areWeAdmin?: boolean;
} = {} } = {}
@ -61,8 +61,8 @@ const renderChange = (
groupMemberships={groupMemberships} groupMemberships={groupMemberships}
groupName={groupName} groupName={groupName}
i18n={i18n} i18n={i18n}
ourACI={OUR_ACI} ourAci={OUR_ACI}
ourPNI={OUR_PNI} ourPni={OUR_PNI}
renderContact={renderContact} renderContact={renderContact}
/> />
); );
@ -94,10 +94,6 @@ export function Multiple(): JSX.Element {
type: 'member-add', type: 'member-add',
uuid: OUR_ACI, uuid: OUR_ACI,
}, },
{
type: 'member-add',
uuid: OUR_PNI,
},
{ {
type: 'description', type: 'description',
description: 'Another description', description: 'Another description',

View file

@ -10,7 +10,11 @@ import type { ReplacementValuesType } from '../../types/I18N';
import type { FullJSXType } from '../Intl'; import type { FullJSXType } from '../Intl';
import { Intl } from '../Intl'; import { Intl } from '../Intl';
import type { LocalizerType } from '../../types/Util'; import type { LocalizerType } from '../../types/Util';
import type { UUIDStringType } from '../../types/UUID'; import type {
AciString,
PniString,
ServiceIdString,
} from '../../types/ServiceId';
import { GroupDescriptionText } from '../GroupDescriptionText'; import { GroupDescriptionText } from '../GroupDescriptionText';
import { Button, ButtonSize, ButtonVariant } from '../Button'; import { Button, ButtonSize, ButtonVariant } from '../Button';
import { SystemMessage } from './SystemMessage'; import { SystemMessage } from './SystemMessage';
@ -26,20 +30,20 @@ export type PropsDataType = {
areWeAdmin: boolean; areWeAdmin: boolean;
conversationId: string; conversationId: string;
groupMemberships?: ReadonlyArray<{ groupMemberships?: ReadonlyArray<{
uuid: UUIDStringType; uuid: AciString;
isAdmin: boolean; isAdmin: boolean;
}>; }>;
groupBannedMemberships?: ReadonlyArray<UUIDStringType>; groupBannedMemberships?: ReadonlyArray<ServiceIdString>;
groupName?: string; groupName?: string;
ourACI?: UUIDStringType; ourAci: AciString | undefined;
ourPNI?: UUIDStringType; ourPni: PniString | undefined;
change: GroupV2ChangeType; change: GroupV2ChangeType;
}; };
export type PropsActionsType = { export type PropsActionsType = {
blockGroupLinkRequests: ( blockGroupLinkRequests: (
conversationId: string, conversationId: string,
uuid: UUIDStringType serviceId: ServiceIdString
) => unknown; ) => unknown;
}; };
@ -108,7 +112,7 @@ const changeToIconMap = new Map<string, GroupIconType>([
function getIcon( function getIcon(
detail: GroupV2ChangeDetailType, detail: GroupV2ChangeDetailType,
isLastText = true, isLastText = true,
fromId?: UUIDStringType fromId?: ServiceIdString
): GroupIconType { ): GroupIconType {
const changeType = detail.type; const changeType = detail.type;
let possibleIcon = changeToIconMap.get(changeType); let possibleIcon = changeToIconMap.get(changeType);
@ -143,29 +147,27 @@ function GroupV2Detail({
groupBannedMemberships, groupBannedMemberships,
groupName, groupName,
i18n, i18n,
ourACI, ourAci,
ourPNI,
renderContact, renderContact,
text, text,
}: { }: {
areWeAdmin: boolean; areWeAdmin: boolean;
blockGroupLinkRequests: ( blockGroupLinkRequests: (
conversationId: string, conversationId: string,
uuid: UUIDStringType uuid: ServiceIdString
) => unknown; ) => unknown;
conversationId: string; conversationId: string;
detail: GroupV2ChangeDetailType; detail: GroupV2ChangeDetailType;
isLastText: boolean; isLastText: boolean;
groupMemberships?: ReadonlyArray<{ groupMemberships?: ReadonlyArray<{
uuid: UUIDStringType; uuid: AciString;
isAdmin: boolean; isAdmin: boolean;
}>; }>;
groupBannedMemberships?: ReadonlyArray<UUIDStringType>; groupBannedMemberships?: ReadonlyArray<ServiceIdString>;
groupName?: string; groupName?: string;
i18n: LocalizerType; i18n: LocalizerType;
fromId?: UUIDStringType; fromId?: ServiceIdString;
ourACI?: UUIDStringType; ourAci: AciString | undefined;
ourPNI?: UUIDStringType;
renderContact: SmartContactRendererType<FullJSXType>; renderContact: SmartContactRendererType<FullJSXType>;
text: FullJSXType; text: FullJSXType;
}): JSX.Element { }): JSX.Element {
@ -260,8 +262,7 @@ function GroupV2Detail({
detail.type === 'admin-approval-bounce' && detail.type === 'admin-approval-bounce' &&
areWeAdmin && areWeAdmin &&
detail.uuid && detail.uuid &&
detail.uuid !== ourACI && detail.uuid !== ourAci &&
detail.uuid !== ourPNI &&
(!fromId || fromId === detail.uuid) && (!fromId || fromId === detail.uuid) &&
!groupMemberships?.some(item => item.uuid === detail.uuid) && !groupMemberships?.some(item => item.uuid === detail.uuid) &&
!groupBannedMemberships?.some(uuid => uuid === detail.uuid) !groupBannedMemberships?.some(uuid => uuid === detail.uuid)
@ -297,8 +298,8 @@ export function GroupV2Change(props: PropsType): ReactElement {
groupMemberships, groupMemberships,
groupName, groupName,
i18n, i18n,
ourACI, ourAci,
ourPNI, ourPni,
renderContact, renderContact,
} = props; } = props;
@ -306,8 +307,8 @@ export function GroupV2Change(props: PropsType): ReactElement {
<> <>
{renderChange<FullJSXType>(change, { {renderChange<FullJSXType>(change, {
i18n, i18n,
ourACI, ourAci,
ourPNI, ourPni,
renderContact, renderContact,
renderString: renderStringToIntl, renderString: renderStringToIntl,
}).map(({ detail, isLastText, text }, index) => { }).map(({ detail, isLastText, text }, index) => {
@ -326,8 +327,7 @@ export function GroupV2Change(props: PropsType): ReactElement {
// Difficult to find a unique key for this type // Difficult to find a unique key for this type
// eslint-disable-next-line react/no-array-index-key // eslint-disable-next-line react/no-array-index-key
key={index} key={index}
ourACI={ourACI} ourAci={ourAci}
ourPNI={ourPNI}
renderContact={renderContact} renderContact={renderContact}
text={text} text={text}
/> />

View file

@ -84,7 +84,7 @@ import type {
import { createRefMerger } from '../../util/refMerger'; import { createRefMerger } from '../../util/refMerger';
import { emojiToData, getEmojiCount, hasNonEmojiText } from '../emoji/lib'; import { emojiToData, getEmojiCount, hasNonEmojiText } from '../emoji/lib';
import { getCustomColorStyle } from '../../util/getCustomColorStyle'; import { getCustomColorStyle } from '../../util/getCustomColorStyle';
import type { UUIDStringType } from '../../types/UUID'; import type { ServiceIdString } from '../../types/ServiceId';
import { DAY, HOUR, MINUTE, SECOND } from '../../util/durations'; import { DAY, HOUR, MINUTE, SECOND } from '../../util/durations';
import { BadgeImageTheme } from '../../badges/BadgeImageTheme'; import { BadgeImageTheme } from '../../badges/BadgeImageTheme';
import { getBadgeImageFileLocalPath } from '../../badges/getBadgeImageFileLocalPath'; import { getBadgeImageFileLocalPath } from '../../badges/getBadgeImageFileLocalPath';
@ -319,7 +319,7 @@ export type PropsActions = {
messageExpanded: (id: string, displayLimit: number) => unknown; messageExpanded: (id: string, displayLimit: number) => unknown;
checkForAccount: (phoneNumber: string) => unknown; checkForAccount: (phoneNumber: string) => unknown;
startConversation: (e164: string, uuid: UUIDStringType) => void; startConversation: (e164: string, uuid: ServiceIdString) => void;
showConversation: ShowConversationType; showConversation: ShowConversationType;
openGiftBadge: (messageId: string) => void; openGiftBadge: (messageId: string) => void;
pushPanelForConversation: PushPanelForConversationActionType; pushPanelForConversation: PushPanelForConversationActionType;

View file

@ -9,8 +9,21 @@ import { MessageBody } from './MessageBody';
import { setupI18n } from '../../util/setupI18n'; import { setupI18n } from '../../util/setupI18n';
import enMessages from '../../../_locales/en/messages.json'; import enMessages from '../../../_locales/en/messages.json';
import { BodyRange } from '../../types/BodyRange'; import { BodyRange } from '../../types/BodyRange';
import { generateAci } from '../../types/ServiceId';
import { RenderLocation } from './MessageTextRenderer'; import { RenderLocation } from './MessageTextRenderer';
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const SERVICE_ID_3 = generateAci();
const SERVICE_ID_4 = generateAci();
const SERVICE_ID_5 = generateAci();
const SERVICE_ID_6 = generateAci();
const SERVICE_ID_7 = generateAci();
const SERVICE_ID_8 = generateAci();
const SERVICE_ID_9 = generateAci();
const SERVICE_ID_10 = generateAci();
const SERVICE_ID_11 = generateAci();
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
export default { export default {
@ -115,7 +128,7 @@ export function Mention(): JSX.Element {
{ {
start: 5, start: 5,
length: 1, length: 1,
mentionUuid: 'tuv', mentionUuid: SERVICE_ID_1,
replacementText: 'Bender B Rodriguez 🤖', replacementText: 'Bender B Rodriguez 🤖',
conversationID: 'x', conversationID: 'x',
}, },
@ -137,21 +150,21 @@ export function MultipleMentions(): JSX.Element {
{ {
start: 2, start: 2,
length: 1, length: 1,
mentionUuid: 'def', mentionUuid: SERVICE_ID_2,
replacementText: 'Philip J Fry', replacementText: 'Philip J Fry',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 4, start: 4,
length: 1, length: 1,
mentionUuid: 'abc', mentionUuid: SERVICE_ID_3,
replacementText: 'Professor Farnsworth', replacementText: 'Professor Farnsworth',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 0, start: 0,
length: 1, length: 1,
mentionUuid: 'xyz', mentionUuid: SERVICE_ID_4,
replacementText: 'Yancy Fry', replacementText: 'Yancy Fry',
conversationID: 'x', conversationID: 'x',
}, },
@ -179,21 +192,21 @@ export function ComplexMessageBody(): JSX.Element {
{ {
start: 78, start: 78,
length: 1, length: 1,
mentionUuid: 'wer', mentionUuid: SERVICE_ID_5,
replacementText: 'Acid Burn', replacementText: 'Acid Burn',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 80, start: 80,
length: 1, length: 1,
mentionUuid: 'xox', mentionUuid: SERVICE_ID_6,
replacementText: 'Cereal Killer', replacementText: 'Cereal Killer',
conversationID: 'x', conversationID: 'x',
}, },
{ {
start: 4, start: 4,
length: 1, length: 1,
mentionUuid: 'ldo', mentionUuid: SERVICE_ID_6,
replacementText: 'Zero Cool', replacementText: 'Zero Cool',
conversationID: 'x', conversationID: 'x',
}, },
@ -311,14 +324,14 @@ export function FormattingSpoiler(): JSX.Element {
{ {
start: 54, start: 54,
length: 1, length: 1,
mentionUuid: 'a', mentionUuid: SERVICE_ID_7,
conversationID: 'a', conversationID: 'a',
replacementText: '🅰️ Alice', replacementText: '🅰️ Alice',
}, },
{ {
start: 60, start: 60,
length: 1, length: 1,
mentionUuid: 'b', mentionUuid: SERVICE_ID_8,
conversationID: 'b', conversationID: 'b',
replacementText: '🅱️ Bob', replacementText: '🅱️ Bob',
}, },
@ -371,35 +384,35 @@ export function FormattingNesting(): JSX.Element {
{ {
start: 29, start: 29,
length: 1, length: 1,
mentionUuid: 'a', mentionUuid: SERVICE_ID_7,
conversationID: 'a', conversationID: 'a',
replacementText: '🅰️ Alice', replacementText: '🅰️ Alice',
}, },
{ {
start: 61, start: 61,
length: 1, length: 1,
mentionUuid: 'b', mentionUuid: SERVICE_ID_8,
conversationID: 'b', conversationID: 'b',
replacementText: '🅱️ Bob', replacementText: '🅱️ Bob',
}, },
{ {
start: 68, start: 68,
length: 1, length: 1,
mentionUuid: 'c', mentionUuid: SERVICE_ID_9,
conversationID: 'c', conversationID: 'c',
replacementText: 'Charlie', replacementText: 'Charlie',
}, },
{ {
start: 80, start: 80,
length: 1, length: 1,
mentionUuid: 'd', mentionUuid: SERVICE_ID_10,
conversationID: 'd', conversationID: 'd',
replacementText: 'Dan', replacementText: 'Dan',
}, },
{ {
start: 105, start: 105,
length: 1, length: 1,
mentionUuid: 'e', mentionUuid: SERVICE_ID_11,
conversationID: 'e', conversationID: 'e',
replacementText: 'Eve', replacementText: 'Eve',
}, },
@ -439,7 +452,7 @@ export function FormattingComplex(): JSX.Element {
{ {
start: 24, start: 24,
length: 1, length: 1,
mentionUuid: 'abc', mentionUuid: SERVICE_ID_3,
conversationID: 'x', conversationID: 'x',
replacementText: '🤖 Hello', replacementText: '🤖 Hello',
}, },
@ -471,7 +484,7 @@ export function FormattingComplex(): JSX.Element {
{ {
start: 491, start: 491,
length: 1, length: 1,
mentionUuid: 'abc', mentionUuid: SERVICE_ID_3,
conversationID: 'x', conversationID: 'x',
replacementText: '🤖 Hello', replacementText: '🤖 Hello',
}, },

View file

@ -11,6 +11,7 @@ import { setupI18n } from '../../util/setupI18n';
import enMessages from '../../../_locales/en/messages.json'; import enMessages from '../../../_locales/en/messages.json';
import type { HydratedBodyRangesType } from '../../types/BodyRange'; import type { HydratedBodyRangesType } from '../../types/BodyRange';
import { BodyRange } from '../../types/BodyRange'; import { BodyRange } from '../../types/BodyRange';
import { generateAci } from '../../types/ServiceId';
import { RenderLocation } from './MessageTextRenderer'; import { RenderLocation } from './MessageTextRenderer';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -91,7 +92,7 @@ export function LongTextWithMention(): JSX.Element {
{ {
start: 800, start: 800,
length: 1, length: 1,
mentionUuid: 'abc', mentionUuid: generateAci(),
conversationID: 'x', conversationID: 'x',
replacementText: 'Alice', replacementText: 'Alice',
}, },

View file

@ -35,6 +35,7 @@ import { getDefaultConversation } from '../../test-both/helpers/getDefaultConver
import { WidthBreakpoint } from '../_util'; import { WidthBreakpoint } from '../_util';
import { DAY, HOUR, MINUTE, SECOND } from '../../util/durations'; import { DAY, HOUR, MINUTE, SECOND } from '../../util/durations';
import { ContactFormType } from '../../types/EmbeddedContact'; import { ContactFormType } from '../../types/EmbeddedContact';
import { generateAci } from '../../types/ServiceId';
import { import {
fakeAttachment, fakeAttachment,
@ -42,7 +43,6 @@ import {
} from '../../test-both/helpers/fakeAttachment'; } from '../../test-both/helpers/fakeAttachment';
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge'; import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
import { ThemeType } from '../../types/Util'; import { ThemeType } from '../../types/Util';
import { UUID } from '../../types/UUID';
import { BadgeCategory } from '../../badges/BadgeCategory'; import { BadgeCategory } from '../../badges/BadgeCategory';
import { PaymentEventKind } from '../../types/Payment'; import { PaymentEventKind } from '../../types/Payment';
@ -1676,7 +1676,7 @@ Mentions.args = {
{ {
start: 0, start: 0,
length: 1, length: 1,
mentionUuid: 'zap', mentionUuid: generateAci(),
replacementText: 'Zapp Brannigan', replacementText: 'Zapp Brannigan',
conversationID: 'x', conversationID: 'x',
}, },
@ -1944,7 +1944,7 @@ EmbeddedContactWithSendMessage.args = {
contact: { contact: {
...fullContact, ...fullContact,
firstNumber: fullContact.number[0].value, firstNumber: fullContact.number[0].value,
uuid: UUID.generate().toString(), uuid: generateAci(),
}, },
direction: 'incoming', direction: 'incoming',
}; };

View file

@ -19,7 +19,7 @@ import { ChooseGroupMembersModal } from './AddGroupMembersModal/ChooseGroupMembe
import { ConfirmAdditionsModal } from './AddGroupMembersModal/ConfirmAdditionsModal'; import { ConfirmAdditionsModal } from './AddGroupMembersModal/ConfirmAdditionsModal';
import { RequestState } from './util'; import { RequestState } from './util';
import { ThemeType } from '../../../types/Util'; import { ThemeType } from '../../../types/Util';
import { makeFakeLookupConversationWithoutUuid } from '../../../test-both/helpers/fakeLookupConversationWithoutUuid'; import { makeFakeLookupConversationWithoutServiceId } from '../../../test-both/helpers/fakeLookupConversationWithoutServiceId';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -30,12 +30,11 @@ export default {
const allCandidateContacts = times(50, () => getDefaultConversation()); const allCandidateContacts = times(50, () => getDefaultConversation());
let allCandidateContactsLookup = makeLookup(allCandidateContacts, 'id'); let allCandidateContactsLookup = makeLookup(allCandidateContacts, 'id');
const lookupConversationWithoutUuid = makeFakeLookupConversationWithoutUuid( const lookupConversationWithoutServiceId =
convo => { makeFakeLookupConversationWithoutServiceId(convo => {
allCandidateContacts.push(convo); allCandidateContacts.push(convo);
allCandidateContactsLookup = makeLookup(allCandidateContacts, 'id'); allCandidateContactsLookup = makeLookup(allCandidateContacts, 'id');
} });
);
type PropsType = ComponentProps<typeof AddGroupMembersModal>; type PropsType = ComponentProps<typeof AddGroupMembersModal>;
@ -68,7 +67,7 @@ const createProps = (
getPreferredBadge={() => undefined} getPreferredBadge={() => undefined}
theme={ThemeType.light} theme={ThemeType.light}
i18n={i18n} i18n={i18n}
lookupConversationWithoutUuid={lookupConversationWithoutUuid} lookupConversationWithoutServiceId={lookupConversationWithoutServiceId}
showUserNotFoundModal={action('showUserNotFoundModal')} showUserNotFoundModal={action('showUserNotFoundModal')}
isUsernamesEnabled isUsernamesEnabled
/> />

View file

@ -17,7 +17,7 @@ import { strictAssert, assertDev } from '../../../../util/assert';
import { refMerger } from '../../../../util/refMerger'; import { refMerger } from '../../../../util/refMerger';
import { useRestoreFocus } from '../../../../hooks/useRestoreFocus'; import { useRestoreFocus } from '../../../../hooks/useRestoreFocus';
import { missingCaseError } from '../../../../util/missingCaseError'; import { missingCaseError } from '../../../../util/missingCaseError';
import type { LookupConversationWithoutUuidActionsType } from '../../../../util/lookupConversationWithoutUuid'; import type { LookupConversationWithoutServiceIdActionsType } from '../../../../util/lookupConversationWithoutServiceId';
import { parseAndFormatPhoneNumber } from '../../../../util/libphonenumberInstance'; import { parseAndFormatPhoneNumber } from '../../../../util/libphonenumberInstance';
import type { ParsedE164Type } from '../../../../util/libphonenumberInstance'; import type { ParsedE164Type } from '../../../../util/libphonenumberInstance';
import { filterAndSortConversationsByRecent } from '../../../../util/filterAndSortConversations'; import { filterAndSortConversationsByRecent } from '../../../../util/filterAndSortConversations';
@ -65,13 +65,13 @@ export type StatePropsType = {
toggleSelectedContact: (conversationId: string) => void; toggleSelectedContact: (conversationId: string) => void;
isUsernamesEnabled: boolean; isUsernamesEnabled: boolean;
} & Pick< } & Pick<
LookupConversationWithoutUuidActionsType, LookupConversationWithoutServiceIdActionsType,
'lookupConversationWithoutUuid' 'lookupConversationWithoutServiceId'
>; >;
type ActionPropsType = Omit< type ActionPropsType = Omit<
LookupConversationWithoutUuidActionsType, LookupConversationWithoutServiceIdActionsType,
'setIsFetchingUUID' | 'lookupConversationWithoutUuid' 'setIsFetchingUUID' | 'lookupConversationWithoutServiceId'
>; >;
type PropsType = StatePropsType & ActionPropsType; type PropsType = StatePropsType & ActionPropsType;
@ -91,7 +91,7 @@ export function ChooseGroupMembersModal({
setSearchTerm, setSearchTerm,
theme, theme,
toggleSelectedContact, toggleSelectedContact,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
isUsernamesEnabled, isUsernamesEnabled,
}: PropsType): JSX.Element { }: PropsType): JSX.Element {
@ -345,7 +345,9 @@ export function ChooseGroupMembersModal({
} }
showUserNotFoundModal={showUserNotFoundModal} showUserNotFoundModal={showUserNotFoundModal}
setIsFetchingUUID={setIsFetchingUUID} setIsFetchingUUID={setIsFetchingUUID}
lookupConversationWithoutUuid={lookupConversationWithoutUuid} lookupConversationWithoutServiceId={
lookupConversationWithoutServiceId
}
/> />
); );
break; break;
@ -353,7 +355,9 @@ export function ChooseGroupMembersModal({
item = ( item = (
<PhoneNumberCheckbox <PhoneNumberCheckbox
phoneNumber={row.phoneNumber} phoneNumber={row.phoneNumber}
lookupConversationWithoutUuid={lookupConversationWithoutUuid} lookupConversationWithoutServiceId={
lookupConversationWithoutServiceId
}
showUserNotFoundModal={showUserNotFoundModal} showUserNotFoundModal={showUserNotFoundModal}
setIsFetchingUUID={setIsFetchingUUID} setIsFetchingUUID={setIsFetchingUUID}
toggleConversationInChooseMembers={conversationId => toggleConversationInChooseMembers={conversationId =>

View file

@ -14,7 +14,7 @@ import { ChooseGroupMembersModal } from './AddGroupMembersModal/ChooseGroupMembe
import { ConfirmAdditionsModal } from './AddGroupMembersModal/ConfirmAdditionsModal'; import { ConfirmAdditionsModal } from './AddGroupMembersModal/ConfirmAdditionsModal';
import type { ConversationType } from '../../../state/ducks/conversations'; import type { ConversationType } from '../../../state/ducks/conversations';
import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation';
import { makeFakeLookupConversationWithoutUuid } from '../../../test-both/helpers/fakeLookupConversationWithoutUuid'; import { makeFakeLookupConversationWithoutServiceId } from '../../../test-both/helpers/fakeLookupConversationWithoutServiceId';
import { ThemeType } from '../../../types/Util'; import { ThemeType } from '../../../types/Util';
import { DurationInSeconds } from '../../../util/durations'; import { DurationInSeconds } from '../../../util/durations';
import { NavTab } from '../../../state/ducks/nav'; import { NavTab } from '../../../state/ducks/nav';
@ -120,7 +120,7 @@ const createProps = (
getPreferredBadge={() => undefined} getPreferredBadge={() => undefined}
theme={ThemeType.light} theme={ThemeType.light}
i18n={i18n} i18n={i18n}
lookupConversationWithoutUuid={makeFakeLookupConversationWithoutUuid()} lookupConversationWithoutServiceId={makeFakeLookupConversationWithoutServiceId()}
showUserNotFoundModal={action('showUserNotFoundModal')} showUserNotFoundModal={action('showUserNotFoundModal')}
isUsernamesEnabled isUsernamesEnabled
/> />

View file

@ -6,7 +6,7 @@ import { times } from 'lodash';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { UUID } from '../../../types/UUID'; import { generateAci } from '../../../types/ServiceId';
import { StorySendMode } from '../../../types/Stories'; import { StorySendMode } from '../../../types/Stories';
import { setupI18n } from '../../../util/setupI18n'; import { setupI18n } from '../../../util/setupI18n';
import enMessages from '../../../../_locales/en/messages.json'; import enMessages from '../../../../_locales/en/messages.json';
@ -45,7 +45,7 @@ const conversation: ConversationType = {
storySendMode: StorySendMode.IfActive, storySendMode: StorySendMode.IfActive,
}; };
const OUR_UUID = UUID.generate().toString(); const OUR_UUID = generateAci();
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
approvePendingMembershipFromGroupV2: action( approvePendingMembershipFromGroupV2: action(
@ -54,7 +54,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
conversation, conversation,
getPreferredBadge: () => undefined, getPreferredBadge: () => undefined,
i18n, i18n,
ourUuid: OUR_UUID, ourAci: OUR_UUID,
pendingApprovalMemberships: times(5, () => ({ pendingApprovalMemberships: times(5, () => ({
member: getDefaultConversation(), member: getDefaultConversation(),
})), })),
@ -68,7 +68,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
...times(8, () => ({ ...times(8, () => ({
member: getDefaultConversation(), member: getDefaultConversation(),
metadata: { metadata: {
addedByUserId: UUID.generate().toString(), addedByUserId: generateAci(),
}, },
})), })),
], ],

View file

@ -7,7 +7,7 @@ import _ from 'lodash';
import type { ConversationType } from '../../../state/ducks/conversations'; import type { ConversationType } from '../../../state/ducks/conversations';
import type { LocalizerType, ThemeType } from '../../../types/Util'; import type { LocalizerType, ThemeType } from '../../../types/Util';
import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges'; import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges';
import type { UUIDStringType } from '../../../types/UUID'; import type { AciString } from '../../../types/ServiceId';
import { Avatar, AvatarSize } from '../../Avatar'; import { Avatar, AvatarSize } from '../../Avatar';
import { ConfirmationDialog } from '../../ConfirmationDialog'; import { ConfirmationDialog } from '../../ConfirmationDialog';
import { PanelSection } from './PanelSection'; import { PanelSection } from './PanelSection';
@ -21,7 +21,7 @@ export type PropsDataType = {
readonly conversation?: ConversationType; readonly conversation?: ConversationType;
readonly getPreferredBadge: PreferredBadgeSelectorType; readonly getPreferredBadge: PreferredBadgeSelectorType;
readonly i18n: LocalizerType; readonly i18n: LocalizerType;
readonly ourUuid: UUIDStringType; readonly ourAci: AciString;
readonly pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>; readonly pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
readonly pendingMemberships: ReadonlyArray<GroupV2PendingMembership>; readonly pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
readonly theme: ThemeType; readonly theme: ThemeType;
@ -42,7 +42,7 @@ export type PropsType = PropsDataType & PropsActionType;
export type GroupV2PendingMembership = { export type GroupV2PendingMembership = {
metadata: { metadata: {
addedByUserId?: UUIDStringType; addedByUserId?: AciString;
}; };
member: ConversationType; member: ConversationType;
}; };
@ -72,16 +72,14 @@ export function PendingInvites({
conversation, conversation,
getPreferredBadge, getPreferredBadge,
i18n, i18n,
ourUuid, ourAci,
pendingMemberships, pendingMemberships,
pendingApprovalMemberships, pendingApprovalMemberships,
revokePendingMembershipsFromGroupV2, revokePendingMembershipsFromGroupV2,
theme, theme,
}: PropsType): JSX.Element { }: PropsType): JSX.Element {
if (!conversation || !ourUuid) { if (!conversation || !ourAci) {
throw new Error( throw new Error('PendingInvites rendered without a conversation or ourAci');
'PendingInvites rendered without a conversation or ourUuid'
);
} }
const [stagedMemberships, setStagedMemberships] = const [stagedMemberships, setStagedMemberships] =
@ -126,7 +124,7 @@ export function PendingInvites({
i18n={i18n} i18n={i18n}
members={conversation.sortedGroupMembers || []} members={conversation.sortedGroupMembers || []}
memberships={pendingMemberships} memberships={pendingMemberships}
ourUuid={ourUuid} ourAci={ourAci}
setStagedMemberships={setStagedMemberships} setStagedMemberships={setStagedMemberships}
theme={theme} theme={theme}
/> />
@ -144,7 +142,7 @@ export function PendingInvites({
i18n={i18n} i18n={i18n}
members={conversation.sortedGroupMembers || []} members={conversation.sortedGroupMembers || []}
onClose={() => setStagedMemberships(null)} onClose={() => setStagedMemberships(null)}
ourUuid={ourUuid} ourAci={ourAci}
revokePendingMembershipsFromGroupV2={ revokePendingMembershipsFromGroupV2={
revokePendingMembershipsFromGroupV2 revokePendingMembershipsFromGroupV2
} }
@ -161,7 +159,7 @@ function MembershipActionConfirmation({
i18n, i18n,
members, members,
onClose, onClose,
ourUuid, ourAci,
revokePendingMembershipsFromGroupV2, revokePendingMembershipsFromGroupV2,
stagedMemberships, stagedMemberships,
}: { }: {
@ -173,7 +171,7 @@ function MembershipActionConfirmation({
i18n: LocalizerType; i18n: LocalizerType;
members: ReadonlyArray<ConversationType>; members: ReadonlyArray<ConversationType>;
onClose: () => void; onClose: () => void;
ourUuid: string; ourAci: AciString;
revokePendingMembershipsFromGroupV2: ( revokePendingMembershipsFromGroupV2: (
conversationId: string, conversationId: string,
memberIds: ReadonlyArray<string> memberIds: ReadonlyArray<string>
@ -234,7 +232,7 @@ function MembershipActionConfirmation({
conversation, conversation,
i18n, i18n,
members, members,
ourUuid, ourAci,
stagedMemberships, stagedMemberships,
})} })}
</ConfirmationDialog> </ConfirmationDialog>
@ -245,13 +243,13 @@ function getConfirmationMessage({
conversation, conversation,
i18n, i18n,
members, members,
ourUuid, ourAci,
stagedMemberships, stagedMemberships,
}: Readonly<{ }: Readonly<{
conversation: ConversationType; conversation: ConversationType;
i18n: LocalizerType; i18n: LocalizerType;
members: ReadonlyArray<ConversationType>; members: ReadonlyArray<ConversationType>;
ourUuid: string; ourAci: AciString;
stagedMemberships: ReadonlyArray<StagedMembershipType>; stagedMemberships: ReadonlyArray<StagedMembershipType>;
}>): string { }>): string {
if (!stagedMemberships || !stagedMemberships.length) { if (!stagedMemberships || !stagedMemberships.length) {
@ -285,7 +283,7 @@ function getConfirmationMessage({
const firstPendingMembership = firstMembership as GroupV2PendingMembership; const firstPendingMembership = firstMembership as GroupV2PendingMembership;
// Pending invite // Pending invite
const invitedByUs = firstPendingMembership.metadata.addedByUserId === ourUuid; const invitedByUs = firstPendingMembership.metadata.addedByUserId === ourAci;
if (invitedByUs) { if (invitedByUs) {
return i18n('icu:PendingInvites--revoke-for', { return i18n('icu:PendingInvites--revoke-for', {
@ -391,7 +389,7 @@ function MembersPendingProfileKey({
i18n, i18n,
members, members,
memberships, memberships,
ourUuid, ourAci,
setStagedMemberships, setStagedMemberships,
getPreferredBadge, getPreferredBadge,
theme, theme,
@ -401,7 +399,7 @@ function MembersPendingProfileKey({
i18n: LocalizerType; i18n: LocalizerType;
members: ReadonlyArray<ConversationType>; members: ReadonlyArray<ConversationType>;
memberships: ReadonlyArray<GroupV2PendingMembership>; memberships: ReadonlyArray<GroupV2PendingMembership>;
ourUuid: string; ourAci: AciString;
setStagedMemberships: (stagedMembership: Array<StagedMembershipType>) => void; setStagedMemberships: (stagedMembership: Array<StagedMembershipType>) => void;
theme: ThemeType; theme: ThemeType;
}>) { }>) {
@ -410,7 +408,7 @@ function MembersPendingProfileKey({
membership => membership.metadata.addedByUserId membership => membership.metadata.addedByUserId
); );
const { [ourUuid]: ourPendingMemberships, ...otherPendingMembershipGroups } = const { [ourAci]: ourPendingMemberships, ...otherPendingMembershipGroups } =
groupedPendingMemberships; groupedPendingMemberships;
const otherPendingMemberships = Object.keys(otherPendingMembershipGroups) const otherPendingMemberships = Object.keys(otherPendingMembershipGroups)

View file

@ -5,6 +5,7 @@ import type { ReactNode, FunctionComponent } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useState } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { isBoolean, isNumber } from 'lodash'; import { isBoolean, isNumber } from 'lodash';
import { v4 as generateUuid } from 'uuid';
import { Avatar, AvatarSize } from '../Avatar'; import { Avatar, AvatarSize } from '../Avatar';
import type { BadgeType } from '../../badges/types'; import type { BadgeType } from '../../badges/types';
@ -16,7 +17,6 @@ import { Spinner } from '../Spinner';
import { Time } from '../Time'; import { Time } from '../Time';
import { formatDateTimeShort } from '../../util/timestamp'; import { formatDateTimeShort } from '../../util/timestamp';
import * as durations from '../../util/durations'; import * as durations from '../../util/durations';
import { UUID } from '../../types/UUID';
const BASE_CLASS_NAME = const BASE_CLASS_NAME =
'module-conversation-list__item--contact-or-conversation'; 'module-conversation-list__item--contact-or-conversation';
@ -113,7 +113,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
} = props; } = props;
const identifier = id ? cleanId(id) : undefined; const identifier = id ? cleanId(id) : undefined;
const htmlId = useMemo(() => UUID.generate().toString(), []); const htmlId = useMemo(() => generateUuid(), []);
const testId = overrideTestId || groupId || uuid; const testId = overrideTestId || groupId || uuid;
const isUnread = isConversationUnread({ markedUnread, unreadCount }); const isUnread = isConversationUnread({ markedUnread, unreadCount });

View file

@ -4,7 +4,7 @@
import React from 'react'; import React from 'react';
import type { ConversationType } from '../../state/ducks/conversations'; import type { ConversationType } from '../../state/ducks/conversations';
import type { LocalizerType } from '../../types/Util'; import type { LocalizerType } from '../../types/Util';
import type { UUIDStringType } from '../../types/UUID'; import type { AciString } from '../../types/ServiceId';
import { Avatar, AvatarSize } from '../Avatar'; import { Avatar, AvatarSize } from '../Avatar';
import { ListTile } from '../ListTile'; import { ListTile } from '../ListTile';
import { UserText } from '../UserText'; import { UserText } from '../UserText';
@ -21,7 +21,7 @@ export type GroupListItemConversationType = Pick<
disabledReason: DisabledReason | undefined; disabledReason: DisabledReason | undefined;
membersCount: number; membersCount: number;
memberships: ReadonlyArray<{ memberships: ReadonlyArray<{
uuid: UUIDStringType; uuid: AciString;
isAdmin: boolean; isAdmin: boolean;
}>; }>;
}; };

View file

@ -13,6 +13,11 @@ import type { PropsType } from './MessageSearchResult';
import { MessageSearchResult } from './MessageSearchResult'; import { MessageSearchResult } from './MessageSearchResult';
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
import { BodyRange } from '../../types/BodyRange'; import { BodyRange } from '../../types/BodyRange';
import { generateAci } from '../../types/ServiceId';
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const SERVICE_ID_3 = generateAci();
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -198,14 +203,14 @@ export function Mention(): JSX.Element {
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8', mentionUuid: SERVICE_ID_3,
replacementText: 'Shoe', replacementText: 'Shoe',
conversationID: 'x', conversationID: 'x',
start: 113, start: 113,
}, },
{ {
length: 1, length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8', mentionUuid: SERVICE_ID_3,
replacementText: 'Shoe', replacementText: 'Shoe',
conversationID: 'x', conversationID: 'x',
start: 237, start: 237,
@ -230,7 +235,7 @@ export function MentionRegexp(): JSX.Element {
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8', mentionUuid: SERVICE_ID_3,
replacementText: 'RegExp', replacementText: 'RegExp',
conversationID: 'x', conversationID: 'x',
start: 0, start: 0,
@ -255,7 +260,7 @@ export function MentionNoMatches(): JSX.Element {
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8', mentionUuid: SERVICE_ID_3,
replacementText: 'Neo', replacementText: 'Neo',
conversationID: 'x', conversationID: 'x',
start: 0, start: 0,
@ -279,14 +284,14 @@ export const _MentionNoMatches = (): JSX.Element => {
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8', mentionUuid: SERVICE_ID_3,
replacementText: 'Shoe', replacementText: 'Shoe',
conversationID: 'x', conversationID: 'x',
start: 113, start: 113,
}, },
{ {
length: 1, length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8', mentionUuid: SERVICE_ID_3,
replacementText: 'Shoe', replacementText: 'Shoe',
conversationID: 'x', conversationID: 'x',
start: 237, start: 237,
@ -311,14 +316,14 @@ export function DoubleMention(): JSX.Element {
bodyRanges: [ bodyRanges: [
{ {
length: 1, length: 1,
mentionUuid: '9eb2eb65-992a-4909-a2a5-18c56bd7648f', mentionUuid: SERVICE_ID_2,
replacementText: 'Alice', replacementText: 'Alice',
conversationID: 'x', conversationID: 'x',
start: 4, start: 4,
}, },
{ {
length: 1, length: 1,
mentionUuid: '755ec61b-1590-48da-b003-3e57b2b54448', mentionUuid: SERVICE_ID_1,
replacementText: 'Bob', replacementText: 'Bob',
conversationID: 'x', conversationID: 'x',
start: 6, start: 6,

View file

@ -10,7 +10,7 @@ import { SPINNER_CLASS_NAME } from './BaseConversationListItem';
import type { ParsedE164Type } from '../../util/libphonenumberInstance'; import type { ParsedE164Type } from '../../util/libphonenumberInstance';
import type { LocalizerType, ThemeType } from '../../types/Util'; import type { LocalizerType, ThemeType } from '../../types/Util';
import { AvatarColors } from '../../types/Colors'; import { AvatarColors } from '../../types/Colors';
import type { LookupConversationWithoutUuidActionsType } from '../../util/lookupConversationWithoutUuid'; import type { LookupConversationWithoutServiceIdActionsType } from '../../util/lookupConversationWithoutServiceId';
import { ListTile } from '../ListTile'; import { ListTile } from '../ListTile';
import { Avatar, AvatarSize } from '../Avatar'; import { Avatar, AvatarSize } from '../Avatar';
import { Spinner } from '../Spinner'; import { Spinner } from '../Spinner';
@ -26,7 +26,7 @@ type PropsHousekeepingType = {
i18n: LocalizerType; i18n: LocalizerType;
theme: ThemeType; theme: ThemeType;
toggleConversationInChooseMembers: (conversationId: string) => void; toggleConversationInChooseMembers: (conversationId: string) => void;
} & LookupConversationWithoutUuidActionsType; } & LookupConversationWithoutServiceIdActionsType;
type PropsType = PropsDataType & PropsHousekeepingType; type PropsType = PropsDataType & PropsHousekeepingType;
@ -36,7 +36,7 @@ export const PhoneNumberCheckbox: FunctionComponent<PropsType> = React.memo(
isChecked, isChecked,
isFetching, isFetching,
i18n, i18n,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
toggleConversationInChooseMembers, toggleConversationInChooseMembers,
@ -52,7 +52,7 @@ export const PhoneNumberCheckbox: FunctionComponent<PropsType> = React.memo(
return; return;
} }
const conversationId = await lookupConversationWithoutUuid({ const conversationId = await lookupConversationWithoutServiceId({
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
@ -67,7 +67,7 @@ export const PhoneNumberCheckbox: FunctionComponent<PropsType> = React.memo(
}, [ }, [
isFetching, isFetching,
toggleConversationInChooseMembers, toggleConversationInChooseMembers,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
setIsModalVisible, setIsModalVisible,

View file

@ -12,7 +12,7 @@ import { Avatar, AvatarSize } from '../Avatar';
import { Spinner } from '../Spinner'; import { Spinner } from '../Spinner';
import type { ParsedE164Type } from '../../util/libphonenumberInstance'; import type { ParsedE164Type } from '../../util/libphonenumberInstance';
import type { LookupConversationWithoutUuidActionsType } from '../../util/lookupConversationWithoutUuid'; import type { LookupConversationWithoutServiceIdActionsType } from '../../util/lookupConversationWithoutServiceId';
import type { LocalizerType } from '../../types/Util'; import type { LocalizerType } from '../../types/Util';
import type { ShowConversationType } from '../../state/ducks/conversations'; import type { ShowConversationType } from '../../state/ducks/conversations';
import { AvatarColors } from '../../types/Colors'; import { AvatarColors } from '../../types/Colors';
@ -25,7 +25,7 @@ type PropsData = {
type PropsHousekeeping = { type PropsHousekeeping = {
i18n: LocalizerType; i18n: LocalizerType;
showConversation: ShowConversationType; showConversation: ShowConversationType;
} & LookupConversationWithoutUuidActionsType; } & LookupConversationWithoutServiceIdActionsType;
export type Props = PropsData & PropsHousekeeping; export type Props = PropsData & PropsHousekeeping;
@ -34,7 +34,7 @@ export const StartNewConversation: FunctionComponent<Props> = React.memo(
i18n, i18n,
phoneNumber, phoneNumber,
isFetching, isFetching,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
showConversation, showConversation,
@ -49,7 +49,7 @@ export const StartNewConversation: FunctionComponent<Props> = React.memo(
if (isFetching) { if (isFetching) {
return; return;
} }
const conversationId = await lookupConversationWithoutUuid({ const conversationId = await lookupConversationWithoutServiceId({
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
@ -63,7 +63,7 @@ export const StartNewConversation: FunctionComponent<Props> = React.memo(
} }
}, [ }, [
showConversation, showConversation,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
setIsModalVisible, setIsModalVisible,

View file

@ -6,7 +6,7 @@ import type { FunctionComponent } from 'react';
import type { LocalizerType, ThemeType } from '../../types/Util'; import type { LocalizerType, ThemeType } from '../../types/Util';
import { AvatarColors } from '../../types/Colors'; import { AvatarColors } from '../../types/Colors';
import type { LookupConversationWithoutUuidActionsType } from '../../util/lookupConversationWithoutUuid'; import type { LookupConversationWithoutServiceIdActionsType } from '../../util/lookupConversationWithoutServiceId';
import { ListTile } from '../ListTile'; import { ListTile } from '../ListTile';
import { Avatar, AvatarSize } from '../Avatar'; import { Avatar, AvatarSize } from '../Avatar';
import { Spinner } from '../Spinner'; import { Spinner } from '../Spinner';
@ -22,7 +22,7 @@ type PropsHousekeepingType = {
i18n: LocalizerType; i18n: LocalizerType;
theme: ThemeType; theme: ThemeType;
toggleConversationInChooseMembers: (conversationId: string) => void; toggleConversationInChooseMembers: (conversationId: string) => void;
} & LookupConversationWithoutUuidActionsType; } & LookupConversationWithoutServiceIdActionsType;
type PropsType = PropsDataType & PropsHousekeepingType; type PropsType = PropsDataType & PropsHousekeepingType;
@ -32,7 +32,7 @@ export const UsernameCheckbox: FunctionComponent<PropsType> = React.memo(
isChecked, isChecked,
isFetching, isFetching,
i18n, i18n,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
toggleConversationInChooseMembers, toggleConversationInChooseMembers,
@ -42,7 +42,7 @@ export const UsernameCheckbox: FunctionComponent<PropsType> = React.memo(
return; return;
} }
const conversationId = await lookupConversationWithoutUuid({ const conversationId = await lookupConversationWithoutServiceId({
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
@ -56,7 +56,7 @@ export const UsernameCheckbox: FunctionComponent<PropsType> = React.memo(
}, [ }, [
isFetching, isFetching,
toggleConversationInChooseMembers, toggleConversationInChooseMembers,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
username, username,

View file

@ -9,7 +9,7 @@ import { Avatar, AvatarSize } from '../Avatar';
import { Spinner } from '../Spinner'; import { Spinner } from '../Spinner';
import type { LocalizerType } from '../../types/Util'; import type { LocalizerType } from '../../types/Util';
import type { LookupConversationWithoutUuidActionsType } from '../../util/lookupConversationWithoutUuid'; import type { LookupConversationWithoutServiceIdActionsType } from '../../util/lookupConversationWithoutServiceId';
import type { ShowConversationType } from '../../state/ducks/conversations'; import type { ShowConversationType } from '../../state/ducks/conversations';
type PropsData = { type PropsData = {
@ -20,14 +20,14 @@ type PropsData = {
type PropsHousekeeping = { type PropsHousekeeping = {
i18n: LocalizerType; i18n: LocalizerType;
showConversation: ShowConversationType; showConversation: ShowConversationType;
} & LookupConversationWithoutUuidActionsType; } & LookupConversationWithoutServiceIdActionsType;
export type Props = PropsData & PropsHousekeeping; export type Props = PropsData & PropsHousekeeping;
export function UsernameSearchResultListItem({ export function UsernameSearchResultListItem({
i18n, i18n,
isFetchingUsername, isFetchingUsername,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
username, username,
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
@ -37,7 +37,7 @@ export function UsernameSearchResultListItem({
if (isFetchingUsername) { if (isFetchingUsername) {
return; return;
} }
const conversationId = await lookupConversationWithoutUuid({ const conversationId = await lookupConversationWithoutServiceId({
showUserNotFoundModal, showUserNotFoundModal,
setIsFetchingUUID, setIsFetchingUUID,
@ -50,7 +50,7 @@ export function UsernameSearchResultListItem({
} }
}, [ }, [
isFetchingUsername, isFetchingUsername,
lookupConversationWithoutUuid, lookupConversationWithoutServiceId,
setIsFetchingUUID, setIsFetchingUUID,
showConversation, showConversation,
showUserNotFoundModal, showUserNotFoundModal,

View file

@ -3,14 +3,16 @@
import type { LocalizerType } from './types/Util'; import type { LocalizerType } from './types/Util';
import type { ReplacementValuesType } from './types/I18N'; import type { ReplacementValuesType } from './types/I18N';
import type { UUIDStringType } from './types/UUID'; import type { ServiceIdString, AciString, PniString } from './types/ServiceId';
import { missingCaseError } from './util/missingCaseError'; import { missingCaseError } from './util/missingCaseError';
import type { GroupV2ChangeDetailType, GroupV2ChangeType } from './groups'; import type { GroupV2ChangeDetailType, GroupV2ChangeType } from './groups';
import { SignalService as Proto } from './protobuf'; import { SignalService as Proto } from './protobuf';
import * as log from './logging/log'; import * as log from './logging/log';
export type SmartContactRendererType<T> = (uuid: UUIDStringType) => T | string; export type SmartContactRendererType<T> = (
serviceId: ServiceIdString
) => T | string;
export type StringRendererType<T> = ( export type StringRendererType<T> = (
id: string, id: string,
i18n: LocalizerType, i18n: LocalizerType,
@ -18,10 +20,11 @@ export type StringRendererType<T> = (
) => T | string; ) => T | string;
export type RenderOptionsType<T> = { export type RenderOptionsType<T> = {
from?: UUIDStringType; // `from` will be a PNI when the change is "declining a PNI invite".
from?: ServiceIdString;
i18n: LocalizerType; i18n: LocalizerType;
ourACI?: UUIDStringType; ourAci: AciString | undefined;
ourPNI?: UUIDStringType; ourPni: PniString | undefined;
renderContact: SmartContactRendererType<T>; renderContact: SmartContactRendererType<T>;
renderString: StringRendererType<T>; renderString: StringRendererType<T>;
}; };
@ -70,8 +73,8 @@ export function renderChangeDetail<T>(
const { const {
from, from,
i18n: localizer, i18n: localizer,
ourACI, ourAci,
ourPNI, ourPni,
renderContact, renderContact,
renderString, renderString,
} = options; } = options;
@ -83,11 +86,11 @@ export function renderChangeDetail<T>(
return renderString(id, localizer, components); return renderString(id, localizer, components);
} }
const isOurUuid = (uuid?: UUIDStringType): boolean => { const isOurUuid = (uuid?: ServiceIdString): boolean => {
if (!uuid) { if (!uuid) {
return false; return false;
} }
return Boolean((ourACI && uuid === ourACI) || (ourPNI && uuid === ourPNI)); return Boolean((ourAci && uuid === ourAci) || (ourPni && uuid === ourPni));
}; };
const fromYou = isOurUuid(from); const fromYou = isOurUuid(from);

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,6 @@ import * as log from '../logging/log';
import { HTTPError } from '../textsecure/Errors'; import { HTTPError } from '../textsecure/Errors';
import { SignalService as Proto } from '../protobuf'; import { SignalService as Proto } from '../protobuf';
import { ToastType } from '../types/Toast'; import { ToastType } from '../types/Toast';
import { UUIDKind } from '../types/UUID';
import { import {
applyNewAvatar, applyNewAvatar,
decryptGroupDescription, decryptGroupDescription,
@ -63,9 +62,9 @@ export async function joinViaLink(hash: string): Promise<void> {
const existingConversation = const existingConversation =
window.ConversationController.get(id) || window.ConversationController.get(id) ||
window.ConversationController.getByDerivedGroupV2Id(id); window.ConversationController.getByDerivedGroupV2Id(id);
const ourUuid = window.textsecure.storage.user.getCheckedUuid(UUIDKind.ACI); const ourAci = window.textsecure.storage.user.getCheckedAci();
if (existingConversation && existingConversation.hasMember(ourUuid)) { if (existingConversation && existingConversation.hasMember(ourAci)) {
log.warn( log.warn(
`joinViaLink/${logId}: Already a member of group, opening conversation` `joinViaLink/${logId}: Already a member of group, opening conversation`
); );
@ -149,7 +148,7 @@ export async function joinViaLink(hash: string): Promise<void> {
if ( if (
approvalRequired && approvalRequired &&
existingConversation && existingConversation &&
existingConversation.isMemberAwaitingApproval(ourUuid) existingConversation.isMemberAwaitingApproval(ourAci)
) { ) {
log.warn( log.warn(
`joinViaLink/${logId}: Already awaiting approval, opening conversation` `joinViaLink/${logId}: Already awaiting approval, opening conversation`
@ -246,9 +245,9 @@ export async function joinViaLink(hash: string): Promise<void> {
// via some other process. If so, just open that conversation. // via some other process. If so, just open that conversation.
if ( if (
targetConversation && targetConversation &&
(targetConversation.hasMember(ourUuid) || (targetConversation.hasMember(ourAci) ||
(approvalRequired && (approvalRequired &&
targetConversation.isMemberAwaitingApproval(ourUuid))) targetConversation.isMemberAwaitingApproval(ourAci)))
) { ) {
log.warn( log.warn(
`joinViaLink/${logId}: User is part of group on second check, opening conversation` `joinViaLink/${logId}: User is part of group on second check, opening conversation`

View file

@ -35,7 +35,7 @@ import { explodePromise } from '../util/explodePromise';
import type { Job } from './Job'; import type { Job } from './Job';
import type { ParsedJob } from './types'; import type { ParsedJob } from './types';
import type SendMessage from '../textsecure/SendMessage'; import type SendMessage from '../textsecure/SendMessage';
import type { UUIDStringType } from '../types/UUID'; import type { ServiceIdString } from '../types/ServiceId';
import { commonShouldJobContinue } from './helpers/commonShouldJobContinue'; import { commonShouldJobContinue } from './helpers/commonShouldJobContinue';
import { sleeper } from '../util/sleeper'; import { sleeper } from '../util/sleeper';
import { receiptSchema, ReceiptType } from '../types/Receipt'; import { receiptSchema, ReceiptType } from '../types/Receipt';
@ -503,7 +503,7 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
} }
} }
} catch (error: unknown) { } catch (error: unknown) {
const untrustedUuids: Array<UUIDStringType> = []; const untrustedServiceIds: Array<ServiceIdString> = [];
const processError = (toProcess: unknown) => { const processError = (toProcess: unknown) => {
if (toProcess instanceof OutgoingIdentityKeyError) { if (toProcess instanceof OutgoingIdentityKeyError) {
@ -512,14 +512,14 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
'private' 'private'
); );
strictAssert(failedConversation, 'Conversation should be created'); strictAssert(failedConversation, 'Conversation should be created');
const uuid = failedConversation.get('uuid'); const serviceId = failedConversation.getServiceId();
if (!uuid) { if (!serviceId) {
log.error( log.error(
`failedConversation: Conversation ${failedConversation.idForLogging()} missing UUID!` `failedConversation: Conversation ${failedConversation.idForLogging()} missing serviceId!`
); );
return; return;
} }
untrustedUuids.push(uuid); untrustedServiceIds.push(serviceId);
} else if (toProcess instanceof SendMessageChallengeError) { } else if (toProcess instanceof SendMessageChallengeError) {
void window.Signal.challengeHandler?.register( void window.Signal.challengeHandler?.register(
{ {
@ -541,29 +541,29 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
(error.errors || []).forEach(processError); (error.errors || []).forEach(processError);
} }
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
if (type === jobSet.ProfileKey) { if (type === jobSet.ProfileKey) {
log.warn( log.warn(
`Cancelling profile share, since there were ${untrustedUuids.length} untrusted send targets.` `Cancelling profile share, since there were ${untrustedServiceIds.length} untrusted send targets.`
); );
return; return;
} }
if (type === jobSet.Receipts) { if (type === jobSet.Receipts) {
log.warn( log.warn(
`Cancelling receipt send, since there were ${untrustedUuids.length} untrusted send targets.` `Cancelling receipt send, since there were ${untrustedServiceIds.length} untrusted send targets.`
); );
return; return;
} }
log.error( log.error(
`Send failed because ${untrustedUuids.length} conversation(s) were untrusted. Adding to verification list.` `Send failed because ${untrustedServiceIds.length} conversation(s) were untrusted. Adding to verification list.`
); );
window.reduxActions.conversations.conversationStoppedByMissingVerification( window.reduxActions.conversations.conversationStoppedByMissingVerification(
{ {
conversationId: conversation.id, conversationId: conversation.id,
untrustedUuids, untrustedServiceIds,
} }
); );
} }

View file

@ -3,11 +3,11 @@
import { isNotNil } from '../../util/isNotNil'; import { isNotNil } from '../../util/isNotNil';
import * as log from '../../logging/log'; import * as log from '../../logging/log';
import type { UUIDStringType } from '../../types/UUID'; import type { ServiceIdString } from '../../types/ServiceId';
export function getUntrustedConversationUuids( export function getUntrustedConversationServiceIds(
recipients: ReadonlyArray<string> recipients: ReadonlyArray<string>
): Array<UUIDStringType> { ): Array<ServiceIdString> {
return recipients return recipients
.map(recipient => { .map(recipient => {
const recipientConversation = window.ConversationController.getOrCreate( const recipientConversation = window.ConversationController.getOrCreate(
@ -19,15 +19,15 @@ export function getUntrustedConversationUuids(
return null; return null;
} }
const uuid = recipientConversation.get('uuid'); const serviceId = recipientConversation.getServiceId();
if (!uuid) { if (!serviceId) {
log.warn( log.warn(
`getUntrustedConversationUuids: Conversation ${recipientConversation.idForLogging()} had no UUID` `getUntrustedConversationServiceIds: Conversation ${recipientConversation.idForLogging()} had no serviceId`
); );
return null; return null;
} }
return uuid; return serviceId;
}) })
.filter(isNotNil); .filter(isNotNil);
} }

View file

@ -23,7 +23,7 @@ import type {
ConversationQueueJobBundle, ConversationQueueJobBundle,
DeleteForEveryoneJobData, DeleteForEveryoneJobData,
} from '../conversationJobQueue'; } from '../conversationJobQueue';
import { getUntrustedConversationUuids } from './getUntrustedConversationUuids'; import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
import { handleMessageSend } from '../../util/handleMessageSend'; import { handleMessageSend } from '../../util/handleMessageSend';
import { isConversationAccepted } from '../../util/isConversationAccepted'; import { isConversationAccepted } from '../../util/isConversationAccepted';
import { isConversationUnregistered } from '../../util/isConversationUnregistered'; import { isConversationUnregistered } from '../../util/isConversationUnregistered';
@ -34,9 +34,9 @@ import type { MessageModel } from '../../models/messages';
import { SendMessageProtoError } from '../../textsecure/Errors'; import { SendMessageProtoError } from '../../textsecure/Errors';
import { strictAssert } from '../../util/assert'; import { strictAssert } from '../../util/assert';
import type { LoggerType } from '../../types/Logging'; import type { LoggerType } from '../../types/Logging';
import type { ServiceIdString } from '../../types/ServiceId';
import { isStory } from '../../messages/helpers'; import { isStory } from '../../messages/helpers';
import { sendToGroup } from '../../util/sendToGroup'; import { sendToGroup } from '../../util/sendToGroup';
import { getTaggedConversationUuid } from '../../util/getConversationUuid';
export async function sendDeleteForEveryone( export async function sendDeleteForEveryone(
conversation: ConversationModel, conversation: ConversationModel,
@ -91,16 +91,20 @@ export async function sendDeleteForEveryone(
); );
const recipients = deletedForEveryoneSendStatus const recipients = deletedForEveryoneSendStatus
? getRecipients(deletedForEveryoneSendStatus) ? getRecipients(deletedForEveryoneSendStatus)
: recipientsFromJob; : recipientsFromJob
.map(recipient => {
return window.ConversationController.get(recipient)?.getServiceId();
})
.filter(isNotNil);
const untrustedUuids = getUntrustedConversationUuids(recipients); const untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification({ window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: conversation.id, conversationId: conversation.id,
untrustedUuids, untrustedServiceIds,
}); });
throw new Error( throw new Error(
`Delete for everyone blocked because ${untrustedUuids.length} conversation(s) were untrusted. Failing this attempt.` `Delete for everyone blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.`
); );
} }
@ -140,9 +144,7 @@ export async function sendDeleteForEveryone(
proto.dataMessage proto.dataMessage
).finish(), ).finish(),
destination: conversation.get('e164'), destination: conversation.get('e164'),
destinationUuid: getTaggedConversationUuid( destinationServiceId: conversation.getServiceId(),
conversation.attributes
),
expirationStartTimestamp: null, expirationStartTimestamp: null,
options: sendOptions, options: sendOptions,
timestamp, timestamp,
@ -191,9 +193,9 @@ export async function sendDeleteForEveryone(
logId, logId,
messageIds, messageIds,
send: async sender => send: async sender =>
sender.sendMessageToIdentifier({ sender.sendMessageToServiceId({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
identifier: conversation.getSendTarget()!, serviceId: conversation.getSendTarget()!,
messageText: undefined, messageText: undefined,
attachments: [], attachments: [],
deletedForEveryoneTimestamp: targetTimestamp, deletedForEveryoneTimestamp: targetTimestamp,
@ -272,7 +274,7 @@ export async function sendDeleteForEveryone(
function getRecipients( function getRecipients(
sendStatusByConversationId: Record<string, boolean> sendStatusByConversationId: Record<string, boolean>
): Array<string> { ): Array<ServiceIdString> {
return Object.entries(sendStatusByConversationId) return Object.entries(sendStatusByConversationId)
.filter(([_, isSent]) => !isSent) .filter(([_, isSent]) => !isSent)
.map(([conversationId]) => { .map(([conversationId]) => {
@ -286,7 +288,7 @@ function getRecipients(
if (recipient.isBlocked()) { if (recipient.isBlocked()) {
return null; return null;
} }
return recipient.get('uuid'); return recipient.getServiceId();
}) })
.filter(isNotNil); .filter(isNotNil);
} }
@ -301,7 +303,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined, deletedForEveryoneFailed: undefined,
}); });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
return; return;
@ -311,8 +313,8 @@ async function updateMessageWithSuccessfulSends(
...message.get('deletedForEveryoneSendStatus'), ...message.get('deletedForEveryoneSendStatus'),
}; };
result.successfulIdentifiers?.forEach(identifier => { result.successfulServiceIds?.forEach(serviceId => {
const conversation = window.ConversationController.get(identifier); const conversation = window.ConversationController.get(serviceId);
if (!conversation) { if (!conversation) {
return; return;
} }
@ -324,7 +326,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined, deletedForEveryoneFailed: undefined,
}); });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
@ -340,6 +342,6 @@ async function updateMessageWithFailure(
message.set({ deletedForEveryoneFailed: true }); message.set({ deletedForEveryoneFailed: true });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }

View file

@ -16,7 +16,7 @@ import type {
ConversationQueueJobBundle, ConversationQueueJobBundle,
DeleteStoryForEveryoneJobData, DeleteStoryForEveryoneJobData,
} from '../conversationJobQueue'; } from '../conversationJobQueue';
import { getUntrustedConversationUuids } from './getUntrustedConversationUuids'; import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
import { handleMessageSend } from '../../util/handleMessageSend'; import { handleMessageSend } from '../../util/handleMessageSend';
import { isConversationAccepted } from '../../util/isConversationAccepted'; import { isConversationAccepted } from '../../util/isConversationAccepted';
import { isConversationUnregistered } from '../../util/isConversationUnregistered'; import { isConversationUnregistered } from '../../util/isConversationUnregistered';
@ -82,14 +82,14 @@ export async function sendDeleteStoryForEveryone(
.filter(([_, isSent]) => !isSent) .filter(([_, isSent]) => !isSent)
.map(([conversationId]) => conversationId); .map(([conversationId]) => conversationId);
const untrustedUuids = getUntrustedConversationUuids(recipientIds); const untrustedServiceIds = getUntrustedConversationServiceIds(recipientIds);
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification({ window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: ourConversation.id, conversationId: ourConversation.id,
untrustedUuids, untrustedServiceIds,
}); });
throw new Error( throw new Error(
`Delete for everyone blocked because ${untrustedUuids.length} ` + `Delete for everyone blocked because ${untrustedServiceIds.length} ` +
'conversation(s) were untrusted. Failing this attempt.' 'conversation(s) were untrusted. Failing this attempt.'
); );
} }
@ -171,10 +171,12 @@ export async function sendDeleteStoryForEveryone(
}); });
try { try {
const serviceId = conversation.getSendTarget();
strictAssert(serviceId, 'conversation has no service id');
await handleMessageSend( await handleMessageSend(
messaging.sendMessageToIdentifier({ messaging.sendMessageToServiceId({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion serviceId,
identifier: conversation.getSendTarget()!,
messageText: undefined, messageText: undefined,
attachments: [], attachments: [],
deletedForEveryoneTimestamp: targetTimestamp, deletedForEveryoneTimestamp: targetTimestamp,
@ -200,7 +202,7 @@ export async function sendDeleteStoryForEveryone(
await updateMessageWithSuccessfulSends(message, { await updateMessageWithSuccessfulSends(message, {
dataMessage: undefined, dataMessage: undefined,
editMessage: undefined, editMessage: undefined,
successfulIdentifiers: [conversation.id], successfulServiceIds: [serviceId],
}); });
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof SendMessageProtoError) { if (error instanceof SendMessageProtoError) {
@ -231,17 +233,15 @@ export async function sendDeleteStoryForEveryone(
syncMessage: true, syncMessage: true,
}); });
const destinationUuid = ourConversation const destinationServiceId = ourConversation.getCheckedServiceId(
.getCheckedUuid('deleteStoryForEveryone') 'deleteStoryForEveryone'
.toString(); );
// Sync message for other devices // Sync message for other devices
await handleMessageSend( await handleMessageSend(
messaging.sendSyncMessage({ messaging.sendSyncMessage({
destination: undefined, destination: undefined,
destinationUuid: { destinationServiceId,
aci: destinationUuid,
},
storyMessageRecipients: updatedStoryRecipients?.map( storyMessageRecipients: updatedStoryRecipients?.map(
({ destinationUuid: legacyDestinationUuid, ...rest }) => { ({ destinationUuid: legacyDestinationUuid, ...rest }) => {
return { return {
@ -278,7 +278,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined, deletedForEveryoneFailed: undefined,
}); });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
return; return;
@ -288,8 +288,8 @@ async function updateMessageWithSuccessfulSends(
...message.get('deletedForEveryoneSendStatus'), ...message.get('deletedForEveryoneSendStatus'),
}; };
result.successfulIdentifiers?.forEach(identifier => { result.successfulServiceIds?.forEach(serviceId => {
const conversation = window.ConversationController.get(identifier); const conversation = window.ConversationController.get(serviceId);
if (!conversation) { if (!conversation) {
return; return;
} }
@ -301,7 +301,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined, deletedForEveryoneFailed: undefined,
}); });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
@ -317,6 +317,6 @@ async function updateMessageWithFailure(
message.set({ deletedForEveryoneFailed: true }); message.set({ deletedForEveryoneFailed: true });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }

View file

@ -20,7 +20,6 @@ import { handleMessageSend } from '../../util/handleMessageSend';
import { isConversationAccepted } from '../../util/isConversationAccepted'; import { isConversationAccepted } from '../../util/isConversationAccepted';
import { isConversationUnregistered } from '../../util/isConversationUnregistered'; import { isConversationUnregistered } from '../../util/isConversationUnregistered';
import { DurationInSeconds } from '../../util/durations'; import { DurationInSeconds } from '../../util/durations';
import { getTaggedConversationUuid } from '../../util/getConversationUuid';
export async function sendDirectExpirationTimerUpdate( export async function sendDirectExpirationTimerUpdate(
conversation: ConversationModel, conversation: ConversationModel,
@ -47,14 +46,12 @@ export async function sendDirectExpirationTimerUpdate(
} }
if (conversation.isUntrusted()) { if (conversation.isUntrusted()) {
const uuid = conversation const serviceId = conversation.getCheckedServiceId(
.getCheckedUuid( 'Expiration timer send blocked: untrusted and missing serviceId!'
'Expiration timer send blocked: untrusted and missing uuid!' );
)
.toString();
window.reduxActions.conversations.conversationStoppedByMissingVerification({ window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: conversation.id, conversationId: conversation.id,
untrustedUuids: [uuid], untrustedServiceIds: [serviceId],
}); });
throw new Error( throw new Error(
'Expiration timer send blocked because conversation is untrusted. Failing this attempt.' 'Expiration timer send blocked because conversation is untrusted. Failing this attempt.'
@ -108,7 +105,7 @@ export async function sendDirectExpirationTimerUpdate(
proto.dataMessage proto.dataMessage
).finish(), ).finish(),
destination: conversation.get('e164'), destination: conversation.get('e164'),
destinationUuid: getTaggedConversationUuid(conversation.attributes), destinationServiceId: conversation.getServiceId(),
expirationStartTimestamp: null, expirationStartTimestamp: null,
options: sendOptions, options: sendOptions,
timestamp, timestamp,
@ -143,7 +140,7 @@ export async function sendDirectExpirationTimerUpdate(
send: async sender => send: async sender =>
sender.sendIndividualProto({ sender.sendIndividualProto({
contentHint, contentHint,
identifier: conversation.getSendTarget(), serviceId: conversation.getSendTarget(),
options: sendOptions, options: sendOptions,
proto, proto,
timestamp, timestamp,

View file

@ -11,6 +11,7 @@ import {
import { wrapWithSyncMessageSend } from '../../util/wrapWithSyncMessageSend'; import { wrapWithSyncMessageSend } from '../../util/wrapWithSyncMessageSend';
import * as Bytes from '../../Bytes'; import * as Bytes from '../../Bytes';
import { strictAssert } from '../../util/assert'; import { strictAssert } from '../../util/assert';
import { isNotNil } from '../../util/isNotNil';
import { ourProfileKeyService } from '../../services/ourProfileKey'; import { ourProfileKeyService } from '../../services/ourProfileKey';
import type { ConversationModel } from '../../models/conversations'; import type { ConversationModel } from '../../models/conversations';
@ -19,7 +20,7 @@ import type {
GroupUpdateJobData, GroupUpdateJobData,
ConversationQueueJobBundle, ConversationQueueJobBundle,
} from '../conversationJobQueue'; } from '../conversationJobQueue';
import { getUntrustedConversationUuids } from './getUntrustedConversationUuids'; import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
import { sendToGroup } from '../../util/sendToGroup'; import { sendToGroup } from '../../util/sendToGroup';
// Note: because we don't have a recipient map, if some sends fail, we will resend this // Note: because we don't have a recipient map, if some sends fail, we will resend this
@ -54,35 +55,37 @@ export async function sendGroupUpdate(
const { groupChangeBase64, recipients: jobRecipients, revision } = data; const { groupChangeBase64, recipients: jobRecipients, revision } = data;
const recipients = jobRecipients.filter(id => { const recipients = jobRecipients
const recipient = window.ConversationController.get(id); .map(id => {
if (!recipient) { const recipient = window.ConversationController.get(id);
return false; if (!recipient) {
} return undefined;
if (recipient.isUnregistered()) { }
log.warn( if (recipient.isUnregistered()) {
`${logId}: dropping unregistered recipient ${recipient.idForLogging()}` log.warn(
); `${logId}: dropping unregistered recipient ${recipient.idForLogging()}`
return false; );
} return undefined;
if (recipient.isBlocked()) { }
log.warn( if (recipient.isBlocked()) {
`${logId}: dropping blocked recipient ${recipient.idForLogging()}` log.warn(
); `${logId}: dropping blocked recipient ${recipient.idForLogging()}`
return false; );
} return undefined;
}
return true; return recipient.getSendTarget();
}); })
.filter(isNotNil);
const untrustedUuids = getUntrustedConversationUuids(recipients); const untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification({ window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: conversation.id, conversationId: conversation.id,
untrustedUuids, untrustedServiceIds,
}); });
throw new Error( throw new Error(
`Group update blocked because ${untrustedUuids.length} conversation(s) were untrusted. Failing this attempt.` `Group update blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.`
); );
} }

View file

@ -50,7 +50,8 @@ import { isConversationUnregistered } from '../../util/isConversationUnregistere
import { isConversationAccepted } from '../../util/isConversationAccepted'; import { isConversationAccepted } from '../../util/isConversationAccepted';
import { sendToGroup } from '../../util/sendToGroup'; import { sendToGroup } from '../../util/sendToGroup';
import type { DurationInSeconds } from '../../util/durations'; import type { DurationInSeconds } from '../../util/durations';
import type { UUIDStringType } from '../../types/UUID'; import type { ServiceIdString } from '../../types/ServiceId';
import { normalizeAci } from '../../types/ServiceId';
import * as Bytes from '../../Bytes'; import * as Bytes from '../../Bytes';
const LONG_ATTACHMENT_LIMIT = 2048; const LONG_ATTACHMENT_LIMIT = 2048;
@ -131,29 +132,29 @@ export async function sendNormalMessage(
try { try {
const { const {
allRecipientIdentifiers, allRecipientServiceIds,
recipientIdentifiersWithoutMe, recipientServiceIdsWithoutMe,
sentRecipientIdentifiers, sentRecipientServiceIds,
untrustedUuids, untrustedServiceIds,
} = getMessageRecipients({ } = getMessageRecipients({
log, log,
message, message,
conversation, conversation,
}); });
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification( window.reduxActions.conversations.conversationStoppedByMissingVerification(
{ {
conversationId: conversation.id, conversationId: conversation.id,
untrustedUuids, untrustedServiceIds,
} }
); );
throw new Error( throw new Error(
`Message ${messageId} sending blocked because ${untrustedUuids.length} conversation(s) were untrusted. Failing this attempt.` `Message ${messageId} sending blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.`
); );
} }
if (!allRecipientIdentifiers.length) { if (!allRecipientServiceIds.length) {
log.warn( log.warn(
`trying to send message ${messageId} but it looks like it was already sent to everyone. This is unexpected, but we're giving up` `trying to send message ${messageId} but it looks like it was already sent to everyone. This is unexpected, but we're giving up`
); );
@ -204,11 +205,11 @@ export async function sendNormalMessage(
let messageSendPromise: Promise<CallbackResultType | void>; let messageSendPromise: Promise<CallbackResultType | void>;
if (recipientIdentifiersWithoutMe.length === 0) { if (recipientServiceIdsWithoutMe.length === 0) {
if ( if (
!isMe(conversation.attributes) && !isMe(conversation.attributes) &&
!isGroup(conversation.attributes) && !isGroup(conversation.attributes) &&
sentRecipientIdentifiers.length === 0 sentRecipientServiceIds.length === 0
) { ) {
log.info( log.info(
'No recipients; not sending to ourselves or to group, and no successful sends. Failing job.' 'No recipients; not sending to ourselves or to group, and no successful sends. Failing job.'
@ -230,12 +231,12 @@ export async function sendNormalMessage(
editedMessageTimestamp, editedMessageTimestamp,
expireTimer, expireTimer,
groupV2: conversation.getGroupV2Info({ groupV2: conversation.getGroupV2Info({
members: recipientIdentifiersWithoutMe, members: recipientServiceIdsWithoutMe,
}), }),
preview, preview,
profileKey, profileKey,
quote, quote,
recipients: allRecipientIdentifiers, recipients: allRecipientServiceIds,
sticker, sticker,
storyContext, storyContext,
timestamp: messageTimestamp, timestamp: messageTimestamp,
@ -255,7 +256,7 @@ export async function sendNormalMessage(
} }
const groupV2Info = conversation.getGroupV2Info({ const groupV2Info = conversation.getGroupV2Info({
members: recipientIdentifiersWithoutMe, members: recipientServiceIdsWithoutMe,
}); });
if (groupV2Info && isNumber(revision)) { if (groupV2Info && isNumber(revision)) {
groupV2Info.revision = revision; groupV2Info.revision = revision;
@ -321,7 +322,7 @@ export async function sendNormalMessage(
} }
log.info('sending direct message'); log.info('sending direct message');
innerPromise = messaging.sendMessageToIdentifier({ innerPromise = messaging.sendMessageToServiceId({
attachments, attachments,
bodyRanges, bodyRanges,
contact, contact,
@ -330,7 +331,7 @@ export async function sendNormalMessage(
editedMessageTimestamp, editedMessageTimestamp,
expireTimer, expireTimer,
groupId: undefined, groupId: undefined,
identifier: recipientIdentifiersWithoutMe[0], serviceId: recipientServiceIdsWithoutMe[0],
messageText: body, messageText: body,
options: sendOptions, options: sendOptions,
preview, preview,
@ -405,15 +406,15 @@ function getMessageRecipients({
conversation: ConversationModel; conversation: ConversationModel;
message: MessageModel; message: MessageModel;
}>): { }>): {
allRecipientIdentifiers: Array<string>; allRecipientServiceIds: Array<ServiceIdString>;
recipientIdentifiersWithoutMe: Array<string>; recipientServiceIdsWithoutMe: Array<ServiceIdString>;
sentRecipientIdentifiers: Array<string>; sentRecipientServiceIds: Array<ServiceIdString>;
untrustedUuids: Array<UUIDStringType>; untrustedServiceIds: Array<ServiceIdString>;
} { } {
const allRecipientIdentifiers: Array<string> = []; const allRecipientServiceIds: Array<ServiceIdString> = [];
const recipientIdentifiersWithoutMe: Array<string> = []; const recipientServiceIdsWithoutMe: Array<ServiceIdString> = [];
const untrustedUuids: Array<UUIDStringType> = []; const untrustedServiceIds: Array<ServiceIdString> = [];
const sentRecipientIdentifiers: Array<string> = []; const sentRecipientServiceIds: Array<ServiceIdString> = [];
const currentConversationRecipients = conversation.getMemberConversationIds(); const currentConversationRecipients = conversation.getMemberConversationIds();
@ -436,14 +437,14 @@ function getMessageRecipients({
} }
if (recipient.isUntrusted()) { if (recipient.isUntrusted()) {
const uuid = recipient.get('uuid'); const serviceId = recipient.getServiceId();
if (!uuid) { if (!serviceId) {
log.error( log.error(
`sendNormalMessage/getMessageRecipients: Untrusted conversation ${recipient.idForLogging()} missing UUID.` `sendNormalMessage/getMessageRecipients: Untrusted conversation ${recipient.idForLogging()} missing serviceId.`
); );
return; return;
} }
untrustedUuids.push(uuid); untrustedServiceIds.push(serviceId);
return; return;
} }
if (recipient.isUnregistered()) { if (recipient.isUnregistered()) {
@ -459,22 +460,22 @@ function getMessageRecipients({
} }
if (isSent(sendState.status)) { if (isSent(sendState.status)) {
sentRecipientIdentifiers.push(recipientIdentifier); sentRecipientServiceIds.push(recipientIdentifier);
return; return;
} }
allRecipientIdentifiers.push(recipientIdentifier); allRecipientServiceIds.push(recipientIdentifier);
if (!isRecipientMe) { if (!isRecipientMe) {
recipientIdentifiersWithoutMe.push(recipientIdentifier); recipientServiceIdsWithoutMe.push(recipientIdentifier);
} }
} }
); );
return { return {
allRecipientIdentifiers, allRecipientServiceIds,
recipientIdentifiersWithoutMe, recipientServiceIdsWithoutMe,
sentRecipientIdentifiers, sentRecipientServiceIds,
untrustedUuids, untrustedServiceIds,
}; };
} }
@ -554,10 +555,23 @@ async function getMessageSendData({
// Save message after uploading attachments // Save message after uploading attachments
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
const storyReaction = message.get('storyReaction'); const storyReaction = message.get('storyReaction');
const storySourceUuid = storyMessage?.get('sourceUuid');
let reactionForSend: ReactionType | undefined;
if (storyReaction) {
const { targetAuthorUuid: targetAuthorAci, ...restOfReaction } =
storyReaction;
reactionForSend = {
...restOfReaction,
targetAuthorAci,
remove: false,
};
}
return { return {
attachments: [ attachments: [
@ -573,17 +587,17 @@ async function getMessageSendData({
messageTimestamp, messageTimestamp,
preview, preview,
quote, quote,
reaction: storyReaction reaction: reactionForSend,
? {
...storyReaction,
remove: false,
}
: undefined,
sticker, sticker,
storyMessage, storyMessage,
storyContext: storyMessage storyContext: storyMessage
? { ? {
authorUuid: storyMessage.get('sourceUuid'), authorAci: storySourceUuid
? normalizeAci(
storySourceUuid,
'sendNormalMessage.storyContext.authorAci'
)
: undefined,
timestamp: storyMessage.get('sent_at'), timestamp: storyMessage.get('sent_at'),
} }
: undefined, : undefined,
@ -702,7 +716,12 @@ async function uploadMessageQuote(
return { return {
isGiftBadge: loadedQuote.isGiftBadge, isGiftBadge: loadedQuote.isGiftBadge,
id: loadedQuote.id, id: loadedQuote.id,
authorUuid: loadedQuote.authorUuid, authorAci: loadedQuote.authorUuid
? normalizeAci(
loadedQuote.authorUuid,
'sendNormalMessage.quote.authorUuid'
)
: undefined,
text: loadedQuote.text, text: loadedQuote.text,
bodyRanges: loadedQuote.bodyRanges, bodyRanges: loadedQuote.bodyRanges,
attachments: attachmentsAfterThumbnailUpload, attachments: attachmentsAfterThumbnailUpload,
@ -919,7 +938,7 @@ async function markMessageFailed(
message.markFailed(); message.markFailed();
void message.saveErrors(errors, { skipSave: true }); void message.saveErrors(errors, { skipSave: true });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }

View file

@ -87,7 +87,7 @@ export async function sendNullMessage(
await handleMessageSend( await handleMessageSend(
messaging.sendIndividualProto({ messaging.sendIndividualProto({
contentHint, contentHint,
identifier: conversation.getSendTarget(), serviceId: conversation.getSendTarget(),
options: sendOptions, options: sendOptions,
proto, proto,
timestamp, timestamp,

View file

@ -124,7 +124,7 @@ export async function sendProfileKey(
}); });
sendPromise = messaging.sendIndividualProto({ sendPromise = messaging.sendIndividualProto({
contentHint, contentHint,
identifier: conversation.getSendTarget(), serviceId: conversation.getSendTarget(),
options: sendOptions, options: sendOptions,
proto, proto,
timestamp, timestamp,
@ -135,8 +135,8 @@ export async function sendProfileKey(
log.error('No revision provided, but conversation is GroupV2'); log.error('No revision provided, but conversation is GroupV2');
} }
const ourUuid = window.textsecure.storage.user.getCheckedUuid(); const ourAci = window.textsecure.storage.user.getCheckedAci();
if (!conversation.hasMember(ourUuid)) { if (!conversation.hasMember(ourAci)) {
log.info( log.info(
`We are not part of group ${conversation.idForLogging()}; refusing to send` `We are not part of group ${conversation.idForLogging()}; refusing to send`
); );

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { isNumber } from 'lodash'; import { isNumber } from 'lodash';
import { v4 as generateUuid } from 'uuid';
import * as Errors from '../../types/errors'; import * as Errors from '../../types/errors';
import { strictAssert } from '../../util/assert'; import { strictAssert } from '../../util/assert';
@ -25,8 +26,7 @@ import { handleMessageSend } from '../../util/handleMessageSend';
import { ourProfileKeyService } from '../../services/ourProfileKey'; import { ourProfileKeyService } from '../../services/ourProfileKey';
import { canReact, isStory } from '../../state/selectors/message'; import { canReact, isStory } from '../../state/selectors/message';
import { findAndFormatContact } from '../../util/findAndFormatContact'; import { findAndFormatContact } from '../../util/findAndFormatContact';
import { UUID } from '../../types/UUID'; import type { ServiceIdString } from '../../types/ServiceId';
import type { UUIDStringType } from '../../types/UUID';
import { handleMultipleSendErrors } from './handleMultipleSendErrors'; import { handleMultipleSendErrors } from './handleMultipleSendErrors';
import { incrementMessageCounter } from '../../util/incrementMessageCounter'; import { incrementMessageCounter } from '../../util/incrementMessageCounter';
@ -51,7 +51,7 @@ export async function sendReaction(
data: ReactionJobData data: ReactionJobData
): Promise<void> { ): Promise<void> {
const { messageId, revision } = data; const { messageId, revision } = data;
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString(); const ourAci = window.textsecure.storage.user.getCheckedAci();
await window.ConversationController.load(); await window.ConversationController.load();
@ -84,7 +84,7 @@ export async function sendReaction(
if (!canReact(message.attributes, ourConversationId, findAndFormatContact)) { if (!canReact(message.attributes, ourConversationId, findAndFormatContact)) {
log.info(`could not react to ${messageId}. Removing this pending reaction`); log.info(`could not react to ${messageId}. Removing this pending reaction`);
markReactionFailed(message, pendingReaction); markReactionFailed(message, pendingReaction);
await window.Signal.Data.saveMessage(message.attributes, { ourUuid }); await window.Signal.Data.saveMessage(message.attributes, { ourAci });
return; return;
} }
@ -93,7 +93,7 @@ export async function sendReaction(
`reacting to message ${messageId} ran out of time. Giving up on sending it` `reacting to message ${messageId} ran out of time. Giving up on sending it`
); );
markReactionFailed(message, pendingReaction); markReactionFailed(message, pendingReaction);
await window.Signal.Data.saveMessage(message.attributes, { ourUuid }); await window.Signal.Data.saveMessage(message.attributes, { ourAci });
return; return;
} }
@ -115,20 +115,20 @@ export async function sendReaction(
const expireTimer = messageConversation.get('expireTimer'); const expireTimer = messageConversation.get('expireTimer');
const { const {
allRecipientIdentifiers, allRecipientServiceIds,
recipientIdentifiersWithoutMe, recipientServiceIdsWithoutMe,
untrustedUuids, untrustedServiceIds,
} = getRecipients(log, pendingReaction, conversation); } = getRecipients(log, pendingReaction, conversation);
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification( window.reduxActions.conversations.conversationStoppedByMissingVerification(
{ {
conversationId: conversation.id, conversationId: conversation.id,
untrustedUuids, untrustedServiceIds,
} }
); );
throw new Error( throw new Error(
`Reaction for message ${messageId} sending blocked because ${untrustedUuids.length} conversation(s) were untrusted. Failing this attempt.` `Reaction for message ${messageId} sending blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.`
); );
} }
@ -136,16 +136,20 @@ export async function sendReaction(
? await ourProfileKeyService.get() ? await ourProfileKeyService.get()
: undefined; : undefined;
const reactionForSend = pendingReaction.emoji const {
? pendingReaction emoji,
: { targetAuthorUuid: targetAuthorAci,
...pendingReaction, ...restOfPendingReaction
emoji: emojiToRemove, } = pendingReaction;
remove: true,
};
const reactionForSend = {
...restOfPendingReaction,
emoji: emoji || emojiToRemove,
targetAuthorAci,
remove: !emoji,
};
const ephemeralMessageForReactionSend = new window.Whisper.Message({ const ephemeralMessageForReactionSend = new window.Whisper.Message({
id: UUID.generate().toString(), id: generateUuid(),
type: 'outgoing', type: 'outgoing',
conversationId: conversation.get('id'), conversationId: conversation.get('id'),
sent_at: pendingReaction.timestamp, sent_at: pendingReaction.timestamp,
@ -166,18 +170,18 @@ export async function sendReaction(
let didFullySend: boolean; let didFullySend: boolean;
const successfulConversationIds = new Set<string>(); const successfulConversationIds = new Set<string>();
if (recipientIdentifiersWithoutMe.length === 0) { if (recipientServiceIdsWithoutMe.length === 0) {
log.info('sending sync reaction message only'); log.info('sending sync reaction message only');
const dataMessage = await messaging.getDataOrEditMessage({ const dataMessage = await messaging.getDataOrEditMessage({
attachments: [], attachments: [],
expireTimer, expireTimer,
groupV2: conversation.getGroupV2Info({ groupV2: conversation.getGroupV2Info({
members: recipientIdentifiersWithoutMe, members: recipientServiceIdsWithoutMe,
}), }),
preview: [], preview: [],
profileKey, profileKey,
reaction: reactionForSend, reaction: reactionForSend,
recipients: allRecipientIdentifiers, recipients: allRecipientServiceIds,
timestamp: pendingReaction.timestamp, timestamp: pendingReaction.timestamp,
}); });
await ephemeralMessageForReactionSend.sendSyncMessageOnly( await ephemeralMessageForReactionSend.sendSyncMessageOnly(
@ -216,8 +220,8 @@ export async function sendReaction(
} }
log.info('sending direct reaction message'); log.info('sending direct reaction message');
promise = messaging.sendMessageToIdentifier({ promise = messaging.sendMessageToServiceId({
identifier: recipientIdentifiersWithoutMe[0], serviceId: recipientServiceIdsWithoutMe[0],
messageText: undefined, messageText: undefined,
attachments: [], attachments: [],
quote: undefined, quote: undefined,
@ -245,7 +249,7 @@ export async function sendReaction(
} }
const groupV2Info = conversation.getGroupV2Info({ const groupV2Info = conversation.getGroupV2Info({
members: recipientIdentifiersWithoutMe, members: recipientServiceIdsWithoutMe,
}); });
if (groupV2Info && isNumber(revision)) { if (groupV2Info && isNumber(revision)) {
groupV2Info.revision = revision; groupV2Info.revision = revision;
@ -316,7 +320,7 @@ export async function sendReaction(
shouldSave: false, shouldSave: false,
}); });
await window.Signal.Data.saveMessage(reactionMessage.attributes, { await window.Signal.Data.saveMessage(reactionMessage.attributes, {
ourUuid, ourAci,
forceSave: true, forceSave: true,
}); });
@ -350,7 +354,7 @@ export async function sendReaction(
toThrow: originalError || thrownError, toThrow: originalError || thrownError,
}); });
} finally { } finally {
await window.Signal.Data.saveMessage(message.attributes, { ourUuid }); await window.Signal.Data.saveMessage(message.attributes, { ourAci });
} }
} }
@ -374,13 +378,13 @@ function getRecipients(
reaction: Readonly<MessageReactionType>, reaction: Readonly<MessageReactionType>,
conversation: ConversationModel conversation: ConversationModel
): { ): {
allRecipientIdentifiers: Array<string>; allRecipientServiceIds: Array<ServiceIdString>;
recipientIdentifiersWithoutMe: Array<string>; recipientServiceIdsWithoutMe: Array<ServiceIdString>;
untrustedUuids: Array<UUIDStringType>; untrustedServiceIds: Array<ServiceIdString>;
} { } {
const allRecipientIdentifiers: Array<string> = []; const allRecipientServiceIds: Array<ServiceIdString> = [];
const recipientIdentifiersWithoutMe: Array<string> = []; const recipientServiceIdsWithoutMe: Array<ServiceIdString> = [];
const untrustedUuids: Array<UUIDStringType> = []; const untrustedServiceIds: Array<ServiceIdString> = [];
const currentConversationRecipients = conversation.getMemberConversationIds(); const currentConversationRecipients = conversation.getMemberConversationIds();
@ -401,14 +405,14 @@ function getRecipients(
} }
if (recipient.isUntrusted()) { if (recipient.isUntrusted()) {
const uuid = recipient.get('uuid'); const serviceId = recipient.getServiceId();
if (!uuid) { if (!serviceId) {
log.error( log.error(
`sendReaction/getRecipients: Untrusted conversation ${recipient.idForLogging()} missing UUID.` `sendReaction/getRecipients: Untrusted conversation ${recipient.idForLogging()} missing serviceId.`
); );
continue; continue;
} }
untrustedUuids.push(uuid); untrustedServiceIds.push(serviceId);
continue; continue;
} }
if (recipient.isUnregistered()) { if (recipient.isUnregistered()) {
@ -418,16 +422,16 @@ function getRecipients(
continue; continue;
} }
allRecipientIdentifiers.push(recipientIdentifier); allRecipientServiceIds.push(recipientIdentifier);
if (!isRecipientMe) { if (!isRecipientMe) {
recipientIdentifiersWithoutMe.push(recipientIdentifier); recipientServiceIdsWithoutMe.push(recipientIdentifier);
} }
} }
return { return {
allRecipientIdentifiers, allRecipientServiceIds,
recipientIdentifiersWithoutMe, recipientServiceIdsWithoutMe,
untrustedUuids, untrustedServiceIds,
}; };
} }

View file

@ -26,13 +26,14 @@ import { drop } from '../../util/drop';
import { strictAssert } from '../../util/assert'; import { strictAssert } from '../../util/assert';
import type { DecryptionErrorEventData } from '../../textsecure/messageReceiverEvents'; import type { DecryptionErrorEventData } from '../../textsecure/messageReceiverEvents';
import type { LoggerType } from '../../types/Logging'; import type { LoggerType } from '../../types/Logging';
import { isAciString } from '../../types/ServiceId';
import { startAutomaticSessionReset } from '../../util/handleRetry'; import { startAutomaticSessionReset } from '../../util/handleRetry';
function failoverToLocalReset( function failoverToLocalReset(
logger: LoggerType, logger: LoggerType,
options: Pick< options: Pick<
DecryptionErrorEventData, DecryptionErrorEventData,
'senderUuid' | 'senderDevice' | 'timestamp' 'senderAci' | 'senderDevice' | 'timestamp'
> >
) { ) {
logger.error('Failing over to local reset'); logger.error('Failing over to local reset');
@ -48,8 +49,14 @@ export async function sendResendRequest(
timeRemaining, timeRemaining,
log, log,
}: ConversationQueueJobBundle, }: ConversationQueueJobBundle,
data: ResendRequestJobData { senderUuid: senderAci, ...restOfData }: ResendRequestJobData
): Promise<void> { ): Promise<void> {
strictAssert(isAciString(senderAci), 'senderUuid is not an ACI');
const data = {
...restOfData,
senderAci,
};
const { const {
contentHint, contentHint,
groupId, groupId,

View file

@ -69,7 +69,7 @@ export async function sendSenderKeyDistribution(
const { groupId } = data; const { groupId } = data;
const group = window.ConversationController.get(groupId); const group = window.ConversationController.get(groupId);
const distributionId = group?.get('senderKeyInfo')?.distributionId; const distributionId = group?.get('senderKeyInfo')?.distributionId;
const uuid = conversation.get('uuid'); const serviceId = conversation.getServiceId();
if (!distributionId) { if (!distributionId) {
log.info( log.info(
@ -78,9 +78,9 @@ export async function sendSenderKeyDistribution(
return; return;
} }
if (!uuid) { if (!serviceId) {
log.info( log.info(
`conversation ${conversation.idForLogging()} was missing uuid, cancelling job.` `conversation ${conversation.idForLogging()} was missing serviceId, cancelling job.`
); );
return; return;
} }
@ -91,7 +91,7 @@ export async function sendSenderKeyDistribution(
{ {
distributionId, distributionId,
groupId, groupId,
identifiers: [uuid], serviceIds: [serviceId],
throwIfNotInDatabase: true, throwIfNotInDatabase: true,
urgent: false, urgent: false,
}, },

View file

@ -19,7 +19,8 @@ import {
SendActionType, SendActionType,
sendStateReducer, sendStateReducer,
} from '../../messages/MessageSendState'; } from '../../messages/MessageSendState';
import type { UUIDStringType } from '../../types/UUID'; import type { ServiceIdString } from '../../types/ServiceId';
import type { StoryDistributionIdString } from '../../types/StoryDistributionId';
import * as Errors from '../../types/errors'; import * as Errors from '../../types/errors';
import type { StoryMessageRecipientsType } from '../../types/Stories'; import type { StoryMessageRecipientsType } from '../../types/Stories';
import dataInterface from '../../sql/Client'; import dataInterface from '../../sql/Client';
@ -34,7 +35,6 @@ import { handleMultipleSendErrors } from './handleMultipleSendErrors';
import { isGroupV2, isMe } from '../../util/whatTypeOfConversation'; import { isGroupV2, isMe } from '../../util/whatTypeOfConversation';
import { ourProfileKeyService } from '../../services/ourProfileKey'; import { ourProfileKeyService } from '../../services/ourProfileKey';
import { sendContentMessageToGroup } from '../../util/sendToGroup'; import { sendContentMessageToGroup } from '../../util/sendToGroup';
import { getTaggedConversationUuid } from '../../util/getConversationUuid';
import { distributionListToSendTarget } from '../../util/distributionListToSendTarget'; import { distributionListToSendTarget } from '../../util/distributionListToSendTarget';
import { uploadAttachment } from '../../util/uploadAttachment'; import { uploadAttachment } from '../../util/uploadAttachment';
import { SendMessageChallengeError } from '../../textsecure/Errors'; import { SendMessageChallengeError } from '../../textsecure/Errors';
@ -190,33 +190,41 @@ export async function sendStory(
}); });
} }
const canReplyUuids = new Set<string>(); const canReplyServiceIds = new Set<ServiceIdString>();
const recipientsByUuid = new Map<string, Set<string>>(); const recipientsByServiceId = new Map<
ServiceIdString,
Set<StoryDistributionIdString>
>();
const sentConversationIds = new Map<string, SendState>(); const sentConversationIds = new Map<string, SendState>();
const sentUuids = new Set<string>(); const sentServiceIds = new Set<ServiceIdString>();
// This function is used to keep track of all the recipients so once we're // This function is used to keep track of all the recipients so once we're
// done with our send we can build up the storyMessageRecipients object for // done with our send we can build up the storyMessageRecipients object for
// sending in the sync message. // sending in the sync message.
function addDistributionListToUuidSent( function addDistributionListToServiceIdSent(
listId: string | undefined, listId: StoryDistributionIdString | undefined,
uuid: string, serviceId: ServiceIdString,
canReply?: boolean canReply?: boolean
): void { ): void {
if (conversation.get('uuid') === uuid) { if (conversation.get('uuid') === serviceId) {
return; return;
} }
const distributionListIds = recipientsByUuid.get(uuid) || new Set<string>(); const distributionListIds =
recipientsByServiceId.get(serviceId) ||
new Set<StoryDistributionIdString>();
if (listId) { if (listId) {
recipientsByUuid.set(uuid, new Set([...distributionListIds, listId])); recipientsByServiceId.set(
serviceId,
new Set([...distributionListIds, listId])
);
} else { } else {
recipientsByUuid.set(uuid, distributionListIds); recipientsByServiceId.set(serviceId, distributionListIds);
} }
if (canReply) { if (canReply) {
canReplyUuids.add(uuid); canReplyServiceIds.add(serviceId);
} }
} }
@ -273,36 +281,36 @@ export async function sendStory(
let originalError: Error | undefined; let originalError: Error | undefined;
const { const {
allRecipientIds, allRecipientServiceIds,
allowedReplyByUuid, allowedReplyByServiceId,
pendingSendRecipientIds, pendingSendRecipientServiceIds,
sentRecipientIds, sentRecipientIds,
untrustedUuids, untrustedServiceIds,
} = getMessageRecipients({ } = getMessageRecipients({
log, log,
message, message,
}); });
try { try {
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification( window.reduxActions.conversations.conversationStoppedByMissingVerification(
{ {
conversationId: conversation.id, conversationId: conversation.id,
distributionId, distributionId,
untrustedUuids, untrustedServiceIds,
} }
); );
throw new Error( throw new Error(
`${logId}: sending blocked because ${untrustedUuids.length} conversation(s) were untrusted. Failing this attempt.` `${logId}: sending blocked because ${untrustedServiceIds.length} conversation(s) were untrusted. Failing this attempt.`
); );
} }
if (!pendingSendRecipientIds.length) { if (!pendingSendRecipientServiceIds.length) {
allRecipientIds.forEach(uuid => allRecipientServiceIds.forEach(serviceId =>
addDistributionListToUuidSent( addDistributionListToServiceIdSent(
listId, listId,
uuid, serviceId,
allowedReplyByUuid.get(uuid) allowedReplyByServiceId.get(serviceId)
) )
); );
return; return;
@ -311,7 +319,7 @@ export async function sendStory(
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
const sendOptions = await getSendOptionsForRecipients( const sendOptions = await getSendOptionsForRecipients(
pendingSendRecipientIds, pendingSendRecipientServiceIds,
{ story: true } { story: true }
); );
@ -332,7 +340,7 @@ export async function sendStory(
const sendTarget = distributionList const sendTarget = distributionList
? distributionListToSendTarget( ? distributionListToSendTarget(
distributionList, distributionList,
pendingSendRecipientIds pendingSendRecipientServiceIds
) )
: conversation.toSenderKeyTarget(); : conversation.toSenderKeyTarget();
@ -344,7 +352,7 @@ export async function sendStory(
contentMessage, contentMessage,
isPartialSend: false, isPartialSend: false,
messageId: undefined, messageId: undefined,
recipients: pendingSendRecipientIds, recipients: pendingSendRecipientServiceIds,
sendOptions, sendOptions,
sendTarget, sendTarget,
sendType: 'story', sendType: 'story',
@ -399,19 +407,19 @@ export async function sendStory(
const recipient = window.ConversationController.get( const recipient = window.ConversationController.get(
recipientConversationId recipientConversationId
); );
const uuid = recipient?.get('uuid'); const serviceId = recipient?.getServiceId();
if (!uuid) { if (!serviceId) {
return; return;
} }
sentUuids.add(uuid); sentServiceIds.add(serviceId);
} }
); );
allRecipientIds.forEach(uuid => { allRecipientServiceIds.forEach(serviceId => {
addDistributionListToUuidSent( addDistributionListToServiceIdSent(
listId, listId,
uuid, serviceId,
allowedReplyByUuid.get(uuid) allowedReplyByServiceId.get(serviceId)
); );
}); });
@ -532,36 +540,27 @@ export async function sendStory(
message.set('sendStateByConversationId', newSendStateByConversationId); message.set('sendStateByConversationId', newSendStateByConversationId);
return window.Signal.Data.saveMessage(message.attributes, { return window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
}) })
); );
// Remove any unsent recipients // Remove any unsent recipients
recipientsByUuid.forEach((_value, uuid) => { recipientsByServiceId.forEach((_value, serviceId) => {
if (sentUuids.has(uuid)) { if (sentServiceIds.has(serviceId)) {
return; return;
} }
recipientsByUuid.delete(uuid); recipientsByServiceId.delete(serviceId);
}); });
// Build up the sync message's storyMessageRecipients and send it // Build up the sync message's storyMessageRecipients and send it
const storyMessageRecipients: StoryMessageRecipientsType = []; const storyMessageRecipients: StoryMessageRecipientsType = [];
recipientsByUuid.forEach((distributionListIds, destinationUuid) => { recipientsByServiceId.forEach((distributionListIds, destinationServiceId) => {
const recipient = window.ConversationController.get(destinationUuid);
if (!recipient) {
return;
}
const taggedUuid = getTaggedConversationUuid(recipient.attributes);
if (!taggedUuid) {
return;
}
storyMessageRecipients.push({ storyMessageRecipients.push({
destinationAci: taggedUuid.aci, destinationServiceId,
destinationPni: taggedUuid.pni,
distributionListIds: Array.from(distributionListIds), distributionListIds: Array.from(distributionListIds),
isAllowedToReply: canReplyUuids.has(destinationUuid), isAllowedToReply: canReplyServiceIds.has(destinationServiceId),
}); });
}); });
@ -577,7 +576,7 @@ export async function sendStory(
await messaging.sendSyncMessage({ await messaging.sendSyncMessage({
// Note: these two fields will be undefined if we're sending to a group // Note: these two fields will be undefined if we're sending to a group
destination: conversation.get('e164'), destination: conversation.get('e164'),
destinationUuid: getTaggedConversationUuid(conversation.attributes), destinationServiceId: conversation.getServiceId(),
storyMessage: originalStoryMessage, storyMessage: originalStoryMessage,
storyMessageRecipients, storyMessageRecipients,
expirationStartTimestamp: null, expirationStartTimestamp: null,
@ -607,17 +606,17 @@ function getMessageRecipients({
log: LoggerType; log: LoggerType;
message: MessageModel; message: MessageModel;
}>): { }>): {
allRecipientIds: Array<string>; allRecipientServiceIds: Array<ServiceIdString>;
allowedReplyByUuid: Map<string, boolean>; allowedReplyByServiceId: Map<ServiceIdString, boolean>;
pendingSendRecipientIds: Array<string>; pendingSendRecipientServiceIds: Array<ServiceIdString>;
sentRecipientIds: Array<string>; sentRecipientIds: Array<string>;
untrustedUuids: Array<UUIDStringType>; untrustedServiceIds: Array<ServiceIdString>;
} { } {
const allRecipientIds: Array<string> = []; const allRecipientServiceIds: Array<ServiceIdString> = [];
const allowedReplyByUuid = new Map<string, boolean>(); const allowedReplyByServiceId = new Map<ServiceIdString, boolean>();
const pendingSendRecipientIds: Array<string> = []; const pendingSendRecipientServiceIds: Array<ServiceIdString> = [];
const sentRecipientIds: Array<string> = []; const sentRecipientIds: Array<string> = [];
const untrustedUuids: Array<UUIDStringType> = []; const untrustedServiceIds: Array<ServiceIdString> = [];
Object.entries(message.get('sendStateByConversationId') || {}).forEach( Object.entries(message.get('sendStateByConversationId') || {}).forEach(
([recipientConversationId, sendState]) => { ([recipientConversationId, sendState]) => {
@ -634,14 +633,14 @@ function getMessageRecipients({
} }
if (recipient.isUntrusted()) { if (recipient.isUntrusted()) {
const uuid = recipient.get('uuid'); const serviceId = recipient.getServiceId();
if (!uuid) { if (!serviceId) {
log.error( log.error(
`stories.sendStory/getMessageRecipients: Untrusted conversation ${recipient.idForLogging()} missing UUID.` `stories.sendStory/getMessageRecipients: Untrusted conversation ${recipient.idForLogging()} missing serviceId.`
); );
return; return;
} }
untrustedUuids.push(uuid); untrustedServiceIds.push(serviceId);
return; return;
} }
if (recipient.isUnregistered()) { if (recipient.isUnregistered()) {
@ -653,11 +652,11 @@ function getMessageRecipients({
return; return;
} }
allowedReplyByUuid.set( allowedReplyByServiceId.set(
recipientSendTarget, recipientSendTarget,
Boolean(sendState.isAllowedToReplyToStory) Boolean(sendState.isAllowedToReplyToStory)
); );
allRecipientIds.push(recipientSendTarget); allRecipientServiceIds.push(recipientSendTarget);
if (sendState.isAlreadyIncludedInAnotherDistributionList) { if (sendState.isAlreadyIncludedInAnotherDistributionList) {
return; return;
@ -668,16 +667,16 @@ function getMessageRecipients({
return; return;
} }
pendingSendRecipientIds.push(recipientSendTarget); pendingSendRecipientServiceIds.push(recipientSendTarget);
} }
); );
return { return {
allRecipientIds, allRecipientServiceIds,
allowedReplyByUuid, allowedReplyByServiceId,
pendingSendRecipientIds, pendingSendRecipientServiceIds,
sentRecipientIds, sentRecipientIds,
untrustedUuids, untrustedServiceIds,
}; };
} }
@ -688,7 +687,7 @@ async function markMessageFailed(
message.markFailed(); message.markFailed();
void message.saveErrors(errors, { skipSave: true }); void message.saveErrors(errors, { skipSave: true });
await window.Signal.Data.saveMessage(message.attributes, { await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }

View file

@ -5,16 +5,16 @@ import type { ConversationModel } from '../../models/conversations';
import type { LoggerType } from '../../types/Logging'; import type { LoggerType } from '../../types/Logging';
import { getRecipients } from '../../util/getRecipients'; import { getRecipients } from '../../util/getRecipients';
import { isConversationAccepted } from '../../util/isConversationAccepted'; import { isConversationAccepted } from '../../util/isConversationAccepted';
import { getUntrustedConversationUuids } from './getUntrustedConversationUuids'; import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
export function shouldSendToConversation( export function shouldSendToConversation(
conversation: ConversationModel, conversation: ConversationModel,
log: LoggerType log: LoggerType
): boolean { ): boolean {
const recipients = getRecipients(conversation.attributes); const recipients = getRecipients(conversation.attributes);
const untrustedUuids = getUntrustedConversationUuids(recipients); const untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
if (untrustedUuids.length) { if (untrustedServiceIds.length) {
log.info( log.info(
`conversation ${conversation.idForLogging()} has untrusted recipients; refusing to send` `conversation ${conversation.idForLogging()} has untrusted recipients; refusing to send`
); );

View file

@ -103,6 +103,13 @@ export class SingleProtoJobQueue extends JobQueue<SingleProtoJobData> {
); );
return; return;
} }
const serviceId = conversation.getServiceId();
if (!serviceId) {
log.info(
`conversation ${conversation.idForLogging()} has no serviceId; refusing to send`
);
return;
}
const proto = Proto.Content.decode(Bytes.fromBase64(protoBase64)); const proto = Proto.Content.decode(Bytes.fromBase64(protoBase64));
const options = await getSendOptions(conversation.attributes, { const options = await getSendOptions(conversation.attributes, {
@ -118,7 +125,7 @@ export class SingleProtoJobQueue extends JobQueue<SingleProtoJobData> {
await handleMessageSend( await handleMessageSend(
messaging.sendIndividualProto({ messaging.sendIndividualProto({
contentHint, contentHint,
identifier, serviceId,
options, options,
proto, proto,
timestamp, timestamp,

View file

@ -347,7 +347,7 @@ async function _runJob(job?: AttachmentDownloadJobType): Promise<void> {
); );
if (message) { if (message) {
await saveMessage(message.attributes, { await saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
@ -418,7 +418,7 @@ async function _finishJob(
if (message) { if (message) {
logger.info(`attachment_downloads/_finishJob for job id: ${id}`); logger.info(`attachment_downloads/_finishJob for job id: ${id}`);
await saveMessage(message.attributes, { await saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }

View file

@ -12,7 +12,7 @@ import { isOutgoing, isStory } from '../state/selectors/message';
import { getOwn } from '../util/getOwn'; import { getOwn } from '../util/getOwn';
import { missingCaseError } from '../util/missingCaseError'; import { missingCaseError } from '../util/missingCaseError';
import { createWaitBatcher } from '../util/waitBatcher'; import { createWaitBatcher } from '../util/waitBatcher';
import type { UUIDStringType } from '../types/UUID'; import type { ServiceIdString } from '../types/ServiceId';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import { import {
SendActionType, SendActionType,
@ -37,7 +37,7 @@ export enum MessageReceiptType {
export type MessageReceiptAttributesType = { export type MessageReceiptAttributesType = {
messageSentAt: number; messageSentAt: number;
receiptTimestamp: number; receiptTimestamp: number;
sourceUuid: UUIDStringType; sourceServiceId: ServiceIdString;
sourceConversationId: string; sourceConversationId: string;
sourceDevice: number; sourceDevice: number;
type: MessageReceiptType; type: MessageReceiptType;
@ -80,7 +80,7 @@ const deleteSentProtoBatcher = createWaitBatcher({
async function getTargetMessage( async function getTargetMessage(
sourceId: string, sourceId: string,
sourceUuid: UUIDStringType, serviceId: ServiceIdString,
messages: ReadonlyArray<MessageAttributesType> messages: ReadonlyArray<MessageAttributesType>
): Promise<MessageModel | null> { ): Promise<MessageModel | null> {
if (messages.length === 0) { if (messages.length === 0) {
@ -94,7 +94,9 @@ async function getTargetMessage(
return window.MessageController.register(message.id, message); return window.MessageController.register(message.id, message);
} }
const groups = await window.Signal.Data.getAllGroupsInvolvingUuid(sourceUuid); const groups = await window.Signal.Data.getAllGroupsInvolvingServiceId(
serviceId
);
const ids = groups.map(item => item.id); const ids = groups.map(item => item.id);
ids.push(sourceId); ids.push(sourceId);
@ -154,9 +156,9 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
return []; return [];
} }
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString(); const ourAci = window.textsecure.storage.user.getCheckedAci();
const sourceUuid = getSourceUuid(message.attributes); const sourceUuid = getSourceUuid(message.attributes);
if (ourUuid !== sourceUuid) { if (ourAci !== sourceUuid) {
return []; return [];
} }
@ -255,14 +257,14 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
type === MessageReceiptType.Read type === MessageReceiptType.Read
) { ) {
const recipient = window.ConversationController.get(sourceConversationId); const recipient = window.ConversationController.get(sourceConversationId);
const recipientUuid = recipient?.get('uuid'); const recipientServiceId = recipient?.getServiceId();
const deviceId = receipt.get('sourceDevice'); const deviceId = receipt.get('sourceDevice');
if (recipientUuid && deviceId) { if (recipientServiceId && deviceId) {
await Promise.all([ await Promise.all([
deleteSentProtoBatcher.add({ deleteSentProtoBatcher.add({
timestamp: messageSentAt, timestamp: messageSentAt,
recipientUuid, recipientServiceId,
deviceId, deviceId,
}), }),
@ -283,7 +285,7 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
async onReceipt(receipt: MessageReceiptModel): Promise<void> { async onReceipt(receipt: MessageReceiptModel): Promise<void> {
const messageSentAt = receipt.get('messageSentAt'); const messageSentAt = receipt.get('messageSentAt');
const sourceConversationId = receipt.get('sourceConversationId'); const sourceConversationId = receipt.get('sourceConversationId');
const sourceUuid = receipt.get('sourceUuid'); const sourceServiceId = receipt.get('sourceServiceId');
const type = receipt.get('type'); const type = receipt.get('type');
try { try {
@ -293,7 +295,7 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
const message = await getTargetMessage( const message = await getTargetMessage(
sourceConversationId, sourceConversationId,
sourceUuid, sourceServiceId,
messages messages
); );
@ -315,7 +317,7 @@ export class MessageReceipts extends Collection<MessageReceiptModel> {
'MessageReceipts: No message for receipt', 'MessageReceipts: No message for receipt',
type, type,
sourceConversationId, sourceConversationId,
sourceUuid, sourceServiceId,
messageSentAt messageSentAt
); );
return; return;

View file

@ -7,10 +7,11 @@ import { Collection, Model } from 'backbone';
import type { ConversationModel } from '../models/conversations'; import type { ConversationModel } from '../models/conversations';
import * as log from '../logging/log'; import * as log from '../logging/log';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import type { AciString } from '../types/ServiceId';
export type MessageRequestAttributesType = { export type MessageRequestAttributesType = {
threadE164?: string; threadE164?: string;
threadUuid?: string; threadAci?: AciString;
groupV2Id?: string; groupV2Id?: string;
type: number; type: number;
}; };
@ -44,7 +45,7 @@ export class MessageRequests extends Collection<MessageRequestModel> {
if (conversation.get('uuid')) { if (conversation.get('uuid')) {
const syncByUuid = this.findWhere({ const syncByUuid = this.findWhere({
threadUuid: conversation.get('uuid'), threadAci: conversation.get('uuid'),
}); });
if (syncByUuid) { if (syncByUuid) {
log.info( log.info(
@ -75,7 +76,7 @@ export class MessageRequests extends Collection<MessageRequestModel> {
async onResponse(sync: MessageRequestModel): Promise<void> { async onResponse(sync: MessageRequestModel): Promise<void> {
try { try {
const threadE164 = sync.get('threadE164'); const threadE164 = sync.get('threadE164');
const threadUuid = sync.get('threadUuid'); const threadAci = sync.get('threadAci');
const groupV2Id = sync.get('groupV2Id'); const groupV2Id = sync.get('groupV2Id');
let conversation; let conversation;
@ -84,17 +85,17 @@ export class MessageRequests extends Collection<MessageRequestModel> {
if (groupV2Id) { if (groupV2Id) {
conversation = window.ConversationController.get(groupV2Id); conversation = window.ConversationController.get(groupV2Id);
} }
if (!conversation && (threadE164 || threadUuid)) { if (!conversation && (threadE164 || threadAci)) {
conversation = window.ConversationController.lookupOrCreate({ conversation = window.ConversationController.lookupOrCreate({
e164: threadE164, e164: threadE164,
uuid: threadUuid, uuid: threadAci,
reason: 'MessageRequests.onResponse', reason: 'MessageRequests.onResponse',
}); });
} }
if (!conversation) { if (!conversation) {
log.warn( log.warn(
`Received message request response for unknown conversation: groupv2(${groupV2Id}) ${threadUuid} ${threadE164}` `Received message request response for unknown conversation: groupv2(${groupV2Id}) ${threadAci} ${threadE164}`
); );
return; return;
} }

View file

@ -11,6 +11,7 @@ import { isMessageUnread } from '../util/isMessageUnread';
import { notificationService } from '../services/notifications'; import { notificationService } from '../services/notifications';
import * as log from '../logging/log'; import * as log from '../logging/log';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import type { AciString } from '../types/ServiceId';
import { StartupQueue } from '../util/StartupQueue'; import { StartupQueue } from '../util/StartupQueue';
import { queueUpdateMessage } from '../util/messageBatcher'; import { queueUpdateMessage } from '../util/messageBatcher';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
@ -18,7 +19,7 @@ import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
export type ReadSyncAttributesType = { export type ReadSyncAttributesType = {
senderId: string; senderId: string;
sender?: string; sender?: string;
senderUuid: string; senderAci: AciString;
timestamp: number; timestamp: number;
readAt: number; readAt: number;
}; };
@ -29,7 +30,7 @@ let singleton: ReadSyncs | undefined;
async function maybeItIsAReactionReadSync(sync: ReadSyncModel): Promise<void> { async function maybeItIsAReactionReadSync(sync: ReadSyncModel): Promise<void> {
const readReaction = await window.Signal.Data.markReactionAsRead( const readReaction = await window.Signal.Data.markReactionAsRead(
sync.get('senderUuid'), sync.get('senderAci'),
Number(sync.get('timestamp')) Number(sync.get('timestamp'))
); );
@ -38,7 +39,7 @@ async function maybeItIsAReactionReadSync(sync: ReadSyncModel): Promise<void> {
'Nothing found for read sync', 'Nothing found for read sync',
sync.get('senderId'), sync.get('senderId'),
sync.get('sender'), sync.get('sender'),
sync.get('senderUuid'), sync.get('senderAci'),
sync.get('timestamp') sync.get('timestamp')
); );
return; return;

View file

@ -7,10 +7,11 @@ import { Collection, Model } from 'backbone';
import type { MessageModel } from '../models/messages'; import type { MessageModel } from '../models/messages';
import * as log from '../logging/log'; import * as log from '../logging/log';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import type { AciString } from '../types/ServiceId';
export type ViewOnceOpenSyncAttributesType = { export type ViewOnceOpenSyncAttributesType = {
source?: string; source?: string;
sourceUuid: string; sourceAci: AciString;
timestamp: number; timestamp: number;
}; };
@ -30,7 +31,7 @@ export class ViewOnceOpenSyncs extends Collection<ViewOnceOpenSyncModel> {
forMessage(message: MessageModel): ViewOnceOpenSyncModel | null { forMessage(message: MessageModel): ViewOnceOpenSyncModel | null {
const syncBySourceUuid = this.find(item => { const syncBySourceUuid = this.find(item => {
return ( return (
item.get('sourceUuid') === message.get('sourceUuid') && item.get('sourceAci') === message.get('sourceUuid') &&
item.get('timestamp') === message.get('sent_at') item.get('timestamp') === message.get('sent_at')
); );
}); });
@ -62,26 +63,24 @@ export class ViewOnceOpenSyncs extends Collection<ViewOnceOpenSyncModel> {
); );
const found = messages.find(item => { const found = messages.find(item => {
const itemSourceUuid = item.sourceUuid; const itemSourceAci = item.sourceUuid;
const syncSourceUuid = sync.get('sourceUuid'); const syncSourceAci = sync.get('sourceAci');
const itemSource = item.source; const itemSource = item.source;
const syncSource = sync.get('source'); const syncSource = sync.get('source');
return Boolean( return Boolean(
(itemSourceUuid && (itemSourceAci && syncSourceAci && itemSourceAci === syncSourceAci) ||
syncSourceUuid &&
itemSourceUuid === syncSourceUuid) ||
(itemSource && syncSource && itemSource === syncSource) (itemSource && syncSource && itemSource === syncSource)
); );
}); });
const syncSource = sync.get('source'); const syncSource = sync.get('source');
const syncSourceUuid = sync.get('sourceUuid'); const syncSourceAci = sync.get('sourceAci');
const syncTimestamp = sync.get('timestamp'); const syncTimestamp = sync.get('timestamp');
const wasMessageFound = Boolean(found); const wasMessageFound = Boolean(found);
log.info('Receive view once open sync:', { log.info('Receive view once open sync:', {
syncSource, syncSource,
syncSourceUuid, syncSourceAci,
syncTimestamp, syncTimestamp,
wasMessageFound, wasMessageFound,
}); });

View file

@ -9,6 +9,7 @@ import type { MessageModel } from '../models/messages';
import { ReadStatus } from '../messages/MessageReadStatus'; import { ReadStatus } from '../messages/MessageReadStatus';
import { markViewed } from '../services/MessageUpdater'; import { markViewed } from '../services/MessageUpdater';
import { isDownloaded } from '../types/Attachment'; import { isDownloaded } from '../types/Attachment';
import type { AciString } from '../types/ServiceId';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import { isIncoming } from '../state/selectors/message'; import { isIncoming } from '../state/selectors/message';
import { notificationService } from '../services/notifications'; import { notificationService } from '../services/notifications';
@ -21,7 +22,7 @@ import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
export type ViewSyncAttributesType = { export type ViewSyncAttributesType = {
senderId: string; senderId: string;
senderE164?: string; senderE164?: string;
senderUuid: string; senderAci: AciString;
timestamp: number; timestamp: number;
viewedAt: number; viewedAt: number;
}; };
@ -84,7 +85,7 @@ export class ViewSyncs extends Collection {
'Nothing found for view sync', 'Nothing found for view sync',
sync.get('senderId'), sync.get('senderId'),
sync.get('senderE164'), sync.get('senderE164'),
sync.get('senderUuid'), sync.get('senderAci'),
sync.get('timestamp') sync.get('timestamp')
); );
return; return;

View file

@ -8,7 +8,7 @@ import type {
MessageAttributesType, MessageAttributesType,
QuotedMessageType, QuotedMessageType,
} from '../model-types.d'; } from '../model-types.d';
import type { UUIDStringType } from '../types/UUID'; import type { ServiceIdString } from '../types/ServiceId';
import { PaymentEventKind } from '../types/Payment'; import { PaymentEventKind } from '../types/Payment';
import type { AnyPaymentEvent } from '../types/Payment'; import type { AnyPaymentEvent } from '../types/Payment';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
@ -193,7 +193,7 @@ export function getSourceDevice(
export function getSourceUuid( export function getSourceUuid(
message: Pick<MessageAttributesType, 'type' | 'sourceUuid'> message: Pick<MessageAttributesType, 'type' | 'sourceUuid'>
): UUIDStringType | undefined { ): ServiceIdString | undefined {
if (isIncoming(message) || isStory(message)) { if (isIncoming(message) || isStory(message)) {
return message.sourceUuid; return message.sourceUuid;
} }
@ -203,7 +203,7 @@ export function getSourceUuid(
); );
} }
return window.textsecure.storage.user.getUuid()?.toString(); return window.textsecure.storage.user.getAci();
} }
export const isCustomError = (e: unknown): e is CustomError => export const isCustomError = (e: unknown): e is CustomError =>

View file

@ -5,7 +5,7 @@ import { get, isEmpty } from 'lodash';
import { getOwn } from '../util/getOwn'; import { getOwn } from '../util/getOwn';
import { map, concat, repeat, zipObject } from '../util/iterables'; import { map, concat, repeat, zipObject } from '../util/iterables';
import { isOutgoing } from '../state/selectors/message'; import { isOutgoing } from '../state/selectors/message';
import type { CustomError, MessageAttributesType } from '../model-types.d'; import type { MessageAttributesType } from '../model-types.d';
import type { SendState, SendStateByConversationId } from './MessageSendState'; import type { SendState, SendStateByConversationId } from './MessageSendState';
import { import {
SendActionType, SendActionType,
@ -13,6 +13,11 @@ import {
SendStatus, SendStatus,
} from './MessageSendState'; } from './MessageSendState';
type LegacyCustomError = Error & {
identifier?: string;
number?: string;
};
/** /**
* This converts legacy message fields, such as `sent_to`, into the new * This converts legacy message fields, such as `sent_to`, into the new
* `sendStateByConversationId` format. These legacy fields aren't typed to prevent their * `sendStateByConversationId` format. These legacy fields aren't typed to prevent their
@ -130,7 +135,7 @@ export function migrateLegacySendAttributes(
} }
function getConversationIdsFromErrors( function getConversationIdsFromErrors(
errors: undefined | ReadonlyArray<CustomError>, errors: undefined | ReadonlyArray<LegacyCustomError>,
getConversation: GetConversationType getConversation: GetConversationType
): Array<string> { ): Array<string> {
const result: Array<string> = []; const result: Array<string> = [];

View file

@ -7,7 +7,7 @@ import pMap from 'p-map';
import { CURRENT_SCHEMA_VERSION } from '../types/Message2'; import { CURRENT_SCHEMA_VERSION } from '../types/Message2';
import { isNotNil } from '../util/isNotNil'; import { isNotNil } from '../util/isNotNil';
import type { MessageAttributesType } from '../model-types.d'; import type { MessageAttributesType } from '../model-types.d';
import type { UUIDStringType } from '../types/UUID'; import type { AciString } from '../types/ServiceId';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
const MAX_CONCURRENCY = 5; const MAX_CONCURRENCY = 5;
@ -33,7 +33,7 @@ export async function migrateMessageData({
) => Promise<Array<MessageAttributesType>>; ) => Promise<Array<MessageAttributesType>>;
saveMessages: ( saveMessages: (
data: ReadonlyArray<MessageAttributesType>, data: ReadonlyArray<MessageAttributesType>,
options: { ourUuid: UUIDStringType } options: { ourAci: AciString }
) => Promise<void>; ) => Promise<void>;
maxVersion?: number; maxVersion?: number;
}>): Promise< }>): Promise<
@ -103,7 +103,7 @@ export async function migrateMessageData({
const saveStartTime = Date.now(); const saveStartTime = Date.now();
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString(); const ourAci = window.textsecure.storage.user.getCheckedAci();
await saveMessages( await saveMessages(
[ [
...upgradedMessages, ...upgradedMessages,
@ -114,7 +114,7 @@ export async function migrateMessageData({
schemaMigrationAttempts: (message.schemaMigrationAttempts ?? 0) + 1, schemaMigrationAttempts: (message.schemaMigrationAttempts ?? 0) + 1,
})), })),
], ],
{ ourUuid } { ourAci }
); );
const saveDuration = Date.now() - saveStartTime; const saveDuration = Date.now() - saveStartTime;

40
ts/model-types.d.ts vendored
View file

@ -8,7 +8,6 @@ import * as Backbone from 'backbone';
import type { GroupV2ChangeType } from './groups'; import type { GroupV2ChangeType } from './groups';
import type { DraftBodyRanges, RawBodyRange } from './types/BodyRange'; import type { DraftBodyRanges, RawBodyRange } from './types/BodyRange';
import type { CustomColorType, ConversationColorType } from './types/Colors'; import type { CustomColorType, ConversationColorType } from './types/Colors';
import type { DeviceType } from './textsecure/Types.d';
import type { SendMessageChallengeData } from './textsecure/Errors'; import type { SendMessageChallengeData } from './textsecure/Errors';
import type { MessageModel } from './models/messages'; import type { MessageModel } from './models/messages';
import type { ConversationModel } from './models/conversations'; import type { ConversationModel } from './models/conversations';
@ -22,7 +21,8 @@ import type { AttachmentDraftType, AttachmentType } from './types/Attachment';
import type { EmbeddedContactType } from './types/EmbeddedContact'; import type { EmbeddedContactType } from './types/EmbeddedContact';
import { SignalService as Proto } from './protobuf'; import { SignalService as Proto } from './protobuf';
import type { AvatarDataType } from './types/Avatar'; import type { AvatarDataType } from './types/Avatar';
import type { UUIDStringType } from './types/UUID'; import type { AciString, PniString, ServiceIdString } from './types/ServiceId';
import type { StoryDistributionIdString } from './types/StoryDistributionId';
import type { ReactionSource } from './reactions/ReactionSource'; import type { ReactionSource } from './reactions/ReactionSource';
import type { SeenStatus } from './MessageSeenStatus'; import type { SeenStatus } from './MessageSeenStatus';
import type { GiftBadgeStates } from './components/conversation/Message'; import type { GiftBadgeStates } from './components/conversation/Message';
@ -47,14 +47,20 @@ export type LastMessageStatus =
| 'read' | 'read'
| 'viewed'; | 'viewed';
export type SenderKeyDeviceType = {
id: number;
identifier: string;
registrationId: number;
};
export type SenderKeyInfoType = { export type SenderKeyInfoType = {
createdAtDate: number; createdAtDate: number;
distributionId: string; distributionId: string;
memberDevices: Array<DeviceType>; memberDevices: Array<SenderKeyDeviceType>;
}; };
export type CustomError = Error & { export type CustomError = Error & {
identifier?: string; serviceId?: ServiceIdString;
number?: string; number?: string;
data?: object; data?: object;
retryAfter?: number; retryAfter?: number;
@ -113,7 +119,7 @@ export type GroupV1Update = {
export type MessageReactionType = { export type MessageReactionType = {
emoji: undefined | string; emoji: undefined | string;
fromId: string; fromId: string;
targetAuthorUuid: string; targetAuthorUuid: AciString;
targetTimestamp: number; targetTimestamp: number;
timestamp: number; timestamp: number;
isSentByConversationId?: Record<string, boolean>; isSentByConversationId?: Record<string, boolean>;
@ -164,7 +170,7 @@ export type MessageAttributesType = {
requiredProtocolVersion?: number; requiredProtocolVersion?: number;
retryOptions?: RetryOptions; retryOptions?: RetryOptions;
sourceDevice?: number; sourceDevice?: number;
storyDistributionListId?: string; storyDistributionListId?: StoryDistributionIdString;
storyId?: string; storyId?: string;
storyReplyContext?: StoryReplyContextType; storyReplyContext?: StoryReplyContextType;
storyRecipientsVersion?: number; storyRecipientsVersion?: number;
@ -203,7 +209,7 @@ export type MessageAttributesType = {
conversationId: string; conversationId: string;
storyReaction?: { storyReaction?: {
emoji: string; emoji: string;
targetAuthorUuid: string; targetAuthorUuid: AciString;
targetTimestamp: number; targetTimestamp: number;
}; };
giftBadge?: { giftBadge?: {
@ -218,7 +224,7 @@ export type MessageAttributesType = {
expireTimer?: DurationInSeconds; expireTimer?: DurationInSeconds;
fromSync?: unknown; fromSync?: unknown;
source?: string; source?: string;
sourceUuid?: string; sourceUuid?: ServiceIdString;
}; };
conversationMerge?: { conversationMerge?: {
renderInfo: ConversationRenderInfoType; renderInfo: ConversationRenderInfoType;
@ -242,7 +248,7 @@ export type MessageAttributesType = {
serverGuid?: string; serverGuid?: string;
serverTimestamp?: number; serverTimestamp?: number;
source?: string; source?: string;
sourceUuid?: UUIDStringType; sourceUuid?: ServiceIdString;
timestamp: number; timestamp: number;
@ -371,8 +377,8 @@ export type ConversationAttributesType = {
version: number; version: number;
// Private core info // Private core info
uuid?: UUIDStringType; uuid?: ServiceIdString;
pni?: UUIDStringType; pni?: PniString;
e164?: string; e164?: string;
// Private other fields // Private other fields
@ -463,7 +469,7 @@ export type ConversationRenderInfoType = Pick<
>; >;
export type GroupV2MemberType = { export type GroupV2MemberType = {
uuid: UUIDStringType; uuid: AciString;
role: MemberRoleEnum; role: MemberRoleEnum;
joinedAtVersion: number; joinedAtVersion: number;
@ -475,19 +481,19 @@ export type GroupV2MemberType = {
}; };
export type GroupV2PendingMemberType = { export type GroupV2PendingMemberType = {
addedByUserId?: UUIDStringType; addedByUserId?: AciString;
uuid: UUIDStringType; uuid: ServiceIdString;
timestamp: number; timestamp: number;
role: MemberRoleEnum; role: MemberRoleEnum;
}; };
export type GroupV2BannedMemberType = { export type GroupV2BannedMemberType = {
uuid: UUIDStringType; uuid: ServiceIdString;
timestamp: number; timestamp: number;
}; };
export type GroupV2PendingAdminApprovalType = { export type GroupV2PendingAdminApprovalType = {
uuid: UUIDStringType; uuid: AciString;
timestamp: number; timestamp: number;
}; };
@ -510,7 +516,7 @@ export type ReactionAttributesType = {
// Necessary to put 1:1 story replies into the right conversation - not the same // Necessary to put 1:1 story replies into the right conversation - not the same
// conversation as the target message! // conversation as the target message!
storyReactionMessage?: MessageModel; storyReactionMessage?: MessageModel;
targetAuthorUuid: string; targetAuthorUuid: AciString;
targetTimestamp: number; targetTimestamp: number;
timestamp: number; timestamp: number;
}; };

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@ import {
pick, pick,
union, union,
} from 'lodash'; } from 'lodash';
import { v4 as generateUuid } from 'uuid';
import type { import type {
CustomError, CustomError,
MessageAttributesType, MessageAttributesType,
@ -40,10 +42,10 @@ import { SendMessageProtoError } from '../textsecure/Errors';
import * as expirationTimer from '../util/expirationTimer'; import * as expirationTimer from '../util/expirationTimer';
import { getUserLanguages } from '../util/userLanguages'; import { getUserLanguages } from '../util/userLanguages';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
import { getTaggedConversationUuid } from '../util/getConversationUuid';
import type { ReactionType } from '../types/Reactions'; import type { ReactionType } from '../types/Reactions';
import { UUID, UUIDKind } from '../types/UUID'; import type { ServiceIdString } from '../types/ServiceId';
import { normalizeServiceId } from '../types/ServiceId';
import * as reactionUtil from '../reactions/util'; import * as reactionUtil from '../reactions/util';
import * as Stickers from '../types/Stickers'; import * as Stickers from '../types/Stickers';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
@ -170,7 +172,6 @@ import {
queueUpdateMessage, queueUpdateMessage,
saveNewMessageBatcher, saveNewMessageBatcher,
} from '../util/messageBatcher'; } from '../util/messageBatcher';
import { normalizeUuid } from '../util/normalizeUuid';
import { getCallHistorySelector } from '../state/selectors/callHistory'; import { getCallHistorySelector } from '../state/selectors/callHistory';
import { getConversationSelector } from '../state/selectors/conversations'; import { getConversationSelector } from '../state/selectors/conversations';
@ -343,7 +344,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
shouldSave?: boolean; shouldSave?: boolean;
} = {} } = {}
): Promise<void> { ): Promise<void> {
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString(); const ourAci = window.textsecure.storage.user.getCheckedAci();
const storyId = this.get('storyId'); const storyId = this.get('storyId');
if (!storyId) { if (!storyId) {
return; return;
@ -377,7 +378,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}, },
}); });
if (shouldSave) { if (shouldSave) {
await window.Signal.Data.saveMessage(this.attributes, { ourUuid }); await window.Signal.Data.saveMessage(this.attributes, { ourAci });
} }
return; return;
} }
@ -396,7 +397,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}, },
}); });
if (shouldSave) { if (shouldSave) {
await window.Signal.Data.saveMessage(this.attributes, { ourUuid }); await window.Signal.Data.saveMessage(this.attributes, { ourAci });
} }
} }
@ -486,12 +487,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const changes = GroupChange.renderChange<string>(change, { const changes = GroupChange.renderChange<string>(change, {
i18n: window.i18n, i18n: window.i18n,
ourACI: window.textsecure.storage.user ourAci: window.textsecure.storage.user.getCheckedAci(),
.getCheckedUuid(UUIDKind.ACI) ourPni: window.textsecure.storage.user.getCheckedPni(),
.toString(),
ourPNI: window.textsecure.storage.user
.getCheckedUuid(UUIDKind.PNI)
.toString(),
renderContact: (conversationId: string) => { renderContact: (conversationId: string) => {
const conversation = const conversation =
window.ConversationController.get(conversationId); window.ConversationController.get(conversationId);
@ -849,13 +846,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}); });
} }
const ourUuid = window.textsecure.storage.user const ourAci = window.textsecure.storage.user.getCheckedAci();
.getCheckedUuid()
.toString();
if ( if (
attributes.type === 'incoming' && attributes.type === 'incoming' &&
attributes.storyReaction.targetAuthorUuid === ourUuid attributes.storyReaction.targetAuthorUuid === ourAci
) { ) {
return window.i18n('icu:Quote__story-reaction-notification--incoming', { return window.i18n('icu:Quote__story-reaction-notification--incoming', {
emoji: attributes.storyReaction.emoji, emoji: attributes.storyReaction.emoji,
@ -1149,7 +1144,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (shouldPersist) { if (shouldPersist) {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
@ -1281,7 +1276,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (!skipSave && !this.doNotSave) { if (!skipSave && !this.doNotSave) {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
} }
@ -1374,7 +1369,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
async jobToInsert => { async jobToInsert => {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
jobToInsert, jobToInsert,
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
); );
@ -1389,7 +1384,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
async jobToInsert => { async jobToInsert => {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
jobToInsert, jobToInsert,
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
); );
@ -1462,7 +1457,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
e => e =>
window.ConversationController.getConversationId( window.ConversationController.getConversationId(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e.identifier || e.number! e.serviceId || e.number!
) === incomingConversationId && ) === incomingConversationId &&
(e.name === 'MessageError' || (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' || e.name === 'OutgoingMessageError' ||
@ -1509,7 +1504,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (!this.doNotSave) { if (!this.doNotSave) {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
@ -1522,17 +1517,17 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const sendIsFinal = !sendIsNotFinal; const sendIsFinal = !sendIsNotFinal;
// Capture successful sends // Capture successful sends
const successfulIdentifiers: Array<string> = const successfulServiceIds: Array<ServiceIdString> =
sendIsFinal && sendIsFinal &&
'successfulIdentifiers' in result.value && 'successfulServiceIds' in result.value &&
Array.isArray(result.value.successfulIdentifiers) Array.isArray(result.value.successfulServiceIds)
? result.value.successfulIdentifiers ? result.value.successfulServiceIds
: []; : [];
const sentToAtLeastOneRecipient = const sentToAtLeastOneRecipient =
result.success || Boolean(successfulIdentifiers.length); result.success || Boolean(successfulServiceIds.length);
successfulIdentifiers.forEach(identifier => { successfulServiceIds.forEach(serviceId => {
const conversation = window.ConversationController.get(identifier); const conversation = window.ConversationController.get(serviceId);
if (!conversation) { if (!conversation) {
return; return;
} }
@ -1588,7 +1583,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
errors.forEach(error => { errors.forEach(error => {
const conversation = const conversation =
window.ConversationController.get(error.identifier) || window.ConversationController.get(error.serviceId) ||
window.ConversationController.get(error.number); window.ConversationController.get(error.number);
if (conversation && !saveErrors && sendIsFinal) { if (conversation && !saveErrors && sendIsFinal) {
@ -1663,7 +1658,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (!this.doNotSave) { if (!this.doNotSave) {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
@ -1726,7 +1721,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
throw error; throw error;
} finally { } finally {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
if (updateLeftPane) { if (updateLeftPane) {
@ -1809,7 +1804,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
...encodedContent, ...encodedContent,
timestamp, timestamp,
destination: conv.get('e164'), destination: conv.get('e164'),
destinationUuid: getTaggedConversationUuid(conv.attributes), destinationServiceId: conv.getServiceId(),
expirationStartTimestamp: expirationStartTimestamp:
this.get('expirationStartTimestamp') || null, this.get('expirationStartTimestamp') || null,
conversationIdsSentTo, conversationIdsSentTo,
@ -1856,7 +1851,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
return result; return result;
}); });
@ -2078,7 +2073,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
); );
originalMessage.set(upgradedMessage); originalMessage.set(upgradedMessage);
await window.Signal.Data.saveMessage(upgradedMessage, { await window.Signal.Data.saveMessage(upgradedMessage, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
} catch (error) { } catch (error) {
@ -2206,17 +2201,15 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
: []; : [];
unidentifiedStatus.forEach( unidentifiedStatus.forEach(
({ destinationUuid, destination, unidentified }) => { ({ destinationServiceId, destination, unidentified }) => {
const identifier = const identifier = destinationServiceId || destination;
destinationUuid?.aci || destinationUuid?.pni || destination;
if (!identifier) { if (!identifier) {
return; return;
} }
const { conversation: destinationConversation } = const destinationConversation =
window.ConversationController.maybeMergeContacts({ window.ConversationController.lookupOrCreate({
aci: destinationUuid?.aci, uuid: destinationServiceId,
pni: destinationUuid?.pni,
e164: destination || undefined, e164: destination || undefined,
reason: `handleDataMessage(${initialMessage.timestamp})`, reason: `handleDataMessage(${initialMessage.timestamp})`,
}); });
@ -2255,7 +2248,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
unidentifiedDeliveries: [...unidentifiedDeliveriesSet], unidentifiedDeliveries: [...unidentifiedDeliveriesSet],
}); });
await window.Signal.Data.saveMessage(toUpdate.attributes, { await window.Signal.Data.saveMessage(toUpdate.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
confirm(); confirm();
@ -2346,9 +2339,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
} }
const ourACI = window.textsecure.storage.user.getCheckedUuid( const ourAci = window.textsecure.storage.user.getCheckedAci();
UUIDKind.ACI
);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const sender = window.ConversationController.lookupOrCreate({ const sender = window.ConversationController.lookupOrCreate({
e164: source, e164: source,
@ -2371,7 +2362,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
const areWeMember = const areWeMember =
!conversation.get('left') && conversation.hasMember(ourACI); !conversation.get('left') && conversation.hasMember(ourAci);
// Drop an incoming GroupV2 message if we or the sender are not part of the group // Drop an incoming GroupV2 message if we or the sender are not part of the group
// after applying the message's associated group changes. // after applying the message's associated group changes.
@ -2379,8 +2370,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
type === 'incoming' && type === 'incoming' &&
!isDirectConversation(conversation.attributes) && !isDirectConversation(conversation.attributes) &&
hasGroupV2Prop && hasGroupV2Prop &&
(!areWeMember || (!areWeMember || (sourceUuid && !conversation.hasMember(sourceUuid)))
(sourceUuid && !conversation.hasMember(new UUID(sourceUuid))))
) { ) {
log.warn( log.warn(
`${idLog}: Received message destined for group, which we or the sender are not a part of. Dropping.` `${idLog}: Received message destined for group, which we or the sender are not a part of. Dropping.`
@ -2409,15 +2399,15 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
// Drop incoming messages to announcement only groups where sender is not admin // Drop incoming messages to announcement only groups where sender is not admin
if ( if (conversation.get('announcementsOnly')) {
conversation.get('announcementsOnly') && const senderServiceId = sender.getServiceId();
!conversation.isAdmin(UUID.checkedLookup(sender?.id)) if (!senderServiceId || !conversation.isAdmin(senderServiceId)) {
) { confirm();
confirm(); return;
return; }
} }
const messageId = message.get('id') || UUID.generate().toString(); const messageId = message.get('id') || generateUuid();
// Send delivery receipts, but only for non-story sealed sender messages // Send delivery receipts, but only for non-story sealed sender messages
// and not for messages from unaccepted conversations // and not for messages from unaccepted conversations
@ -2451,7 +2441,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (storyContext) { if (storyContext) {
storyContextLogId = storyContextLogId =
`storyContext(${storyContext.sentTimestamp}, ` + `storyContext(${storyContext.sentTimestamp}, ` +
`${storyContext.authorUuid})`; `${storyContext.authorAci})`;
} }
const [quote, storyQuotes] = await Promise.all([ const [quote, storyQuotes] = await Promise.all([
@ -2466,7 +2456,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const storyQuoteIsFromSelf = const storyQuoteIsFromSelf =
candidateQuote.get('sourceUuid') === candidateQuote.get('sourceUuid') ===
window.storage.user.getCheckedUuid().toString(); window.storage.user.getCheckedAci();
if (!storyQuoteIsFromSelf) { if (!storyQuoteIsFromSelf) {
return true; return true;
@ -2581,13 +2571,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
); );
} }
const ourPNI = window.textsecure.storage.user.getCheckedUuid( const ourPni = window.textsecure.storage.user.getCheckedPni();
UUIDKind.PNI const ourUuids: Set<ServiceIdString> = new Set([ourAci, ourPni]);
);
const ourUuids: Set<string> = new Set([
ourACI.toString(),
ourPNI.toString(),
]);
message.set({ message.set({
id: messageId, id: messageId,
@ -2609,7 +2594,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
return false; return false;
} }
return ourUuids.has( return ourUuids.has(
normalizeUuid( normalizeServiceId(
bodyRange.mentionUuid, bodyRange.mentionUuid,
'handleDataMessage: mentionsMe check' 'handleDataMessage: mentionsMe check'
) )
@ -2737,8 +2722,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const { profileKey } = initialMessage; const { profileKey } = initialMessage;
if ( if (
source === window.textsecure.storage.user.getNumber() || source === window.textsecure.storage.user.getNumber() ||
sourceUuid === sourceUuid === window.textsecure.storage.user.getAci()
window.textsecure.storage.user.getUuid()?.toString()
) { ) {
conversation.set({ profileSharing: true }); conversation.set({ profileSharing: true });
} else if (isDirectConversation(conversation.attributes)) { } else if (isDirectConversation(conversation.attributes)) {
@ -3026,7 +3010,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}); });
// Note: generatedMessage comes with an id, so we have to force this save // Note: generatedMessage comes with an id, so we have to force this save
await window.Signal.Data.saveMessage(generatedMessage.attributes, { await window.Signal.Data.saveMessage(generatedMessage.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
forceSave: true, forceSave: true,
}); });
@ -3112,7 +3096,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
await window.Signal.Data.removeReactionFromConversation({ await window.Signal.Data.removeReactionFromConversation({
emoji: reaction.get('emoji'), emoji: reaction.get('emoji'),
fromId: reaction.get('fromId'), fromId: reaction.get('fromId'),
targetAuthorUuid: reaction.get('targetAuthorUuid'), targetAuthorServiceId: reaction.get('targetAuthorUuid'),
targetTimestamp: reaction.get('targetTimestamp'), targetTimestamp: reaction.get('targetTimestamp'),
}); });
} else { } else {
@ -3182,7 +3166,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
shouldSave: false, shouldSave: false,
}); });
await window.Signal.Data.saveMessage(generatedMessage.attributes, { await window.Signal.Data.saveMessage(generatedMessage.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
forceSave: true, forceSave: true,
}); });
@ -3216,7 +3200,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
); );
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
jobToInsert, jobToInsert,
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
}); });
} else { } else {
@ -3224,7 +3208,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
} else if (shouldPersist && !isStory(this.attributes)) { } else if (shouldPersist && !isStory(this.attributes)) {
await window.Signal.Data.saveMessage(this.attributes, { await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
} }
} }

View file

@ -8,6 +8,7 @@ import Parchment from 'parchment';
import Quill from 'quill'; import Quill from 'quill';
import { render } from 'react-dom'; import { render } from 'react-dom';
import { Emojify } from '../../components/conversation/Emojify'; import { Emojify } from '../../components/conversation/Emojify';
import { normalizeAci } from '../../types/ServiceId';
import type { MentionBlotValue } from '../util'; import type { MentionBlotValue } from '../util';
declare class QuillEmbed extends Parchment.Embed { declare class QuillEmbed extends Parchment.Embed {
@ -40,7 +41,7 @@ export class MentionBlot extends Embed {
} }
return { return {
uuid, uuid: normalizeAci(uuid, 'quill mention blot'),
title, title,
}; };
} }

View file

@ -15,9 +15,10 @@ import { BodyRange } from '../types/BodyRange';
import type { MentionBlot } from './mentions/blot'; import type { MentionBlot } from './mentions/blot';
import { isNewlineOnlyOp, QuillFormattingStyle } from './formatting/menu'; import { isNewlineOnlyOp, QuillFormattingStyle } from './formatting/menu';
import { isNotNil } from '../util/isNotNil'; import { isNotNil } from '../util/isNotNil';
import type { AciString } from '../types/ServiceId';
export type MentionBlotValue = { export type MentionBlotValue = {
uuid: string; uuid: AciString;
title: string; title: string;
}; };

View file

@ -1,6 +1,8 @@
// Copyright 2021 Signal Messenger, LLC // Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { v4 as generateUuid } from 'uuid';
import { ReactionModel } from '../messageModifiers/Reactions'; import { ReactionModel } from '../messageModifiers/Reactions';
import { ReactionSource } from './ReactionSource'; import { ReactionSource } from './ReactionSource';
import { getMessageById } from '../messages/getMessageById'; import { getMessageById } from '../messages/getMessageById';
@ -10,8 +12,8 @@ import { isDirectConversation } from '../util/whatTypeOfConversation';
import { incrementMessageCounter } from '../util/incrementMessageCounter'; import { incrementMessageCounter } from '../util/incrementMessageCounter';
import { repeat, zipObject } from '../util/iterables'; import { repeat, zipObject } from '../util/iterables';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
import { isAciString } from '../types/ServiceId';
import { SendStatus } from '../messages/MessageSendState'; import { SendStatus } from '../messages/MessageSendState';
import { UUID } from '../types/UUID';
import * as log from '../logging/log'; import * as log from '../logging/log';
export async function enqueueReactionForSend({ export async function enqueueReactionForSend({
@ -31,6 +33,10 @@ export async function enqueueReactionForSend({
targetAuthorUuid, targetAuthorUuid,
`enqueueReactionForSend: message ${message.idForLogging()} had no source UUID` `enqueueReactionForSend: message ${message.idForLogging()} had no source UUID`
); );
strictAssert(
isAciString(targetAuthorUuid),
`enqueueReactionForSend: message ${message.idForLogging()} had no source ACI`
);
const targetTimestamp = getMessageSentTimestamp(message.attributes, { const targetTimestamp = getMessageSentTimestamp(message.attributes, {
log, log,
@ -68,7 +74,7 @@ export async function enqueueReactionForSend({
// Only used in story scenarios, where we use a whole message to represent the reaction // Only used in story scenarios, where we use a whole message to represent the reaction
const storyReactionMessage = storyMessage const storyReactionMessage = storyMessage
? new window.Whisper.Message({ ? new window.Whisper.Message({
id: UUID.generate().toString(), id: generateUuid(),
type: 'outgoing', type: 'outgoing',
conversationId: targetConversation.id, conversationId: targetConversation.id,
sent_at: timestamp, sent_at: timestamp,

View file

@ -70,7 +70,7 @@ import {
findBestMatchingCameraId, findBestMatchingCameraId,
} from '../calling/findBestMatchingDevice'; } from '../calling/findBestMatchingDevice';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType } from '../types/Util';
import { UUID, UUIDKind } from '../types/UUID'; import { normalizeAci, isAciString } from '../types/ServiceId';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import type { ConversationModel } from '../models/conversations'; import type { ConversationModel } from '../models/conversations';
import * as Bytes from '../Bytes'; import * as Bytes from '../Bytes';
@ -381,13 +381,13 @@ export class CallingClass {
} }
private attemptToGiveOurUuidToRingRtc(): void { private attemptToGiveOurUuidToRingRtc(): void {
const ourUuid = window.textsecure.storage.user.getUuid()?.toString(); const ourAci = window.textsecure.storage.user.getAci();
if (!ourUuid) { if (!ourAci) {
// This can happen if we're not linked. It's okay if we hit this case. // This can happen if we're not linked. It's okay if we hit this case.
return; return;
} }
RingRTC.setSelfUuid(Buffer.from(uuidToBytes(ourUuid))); RingRTC.setSelfUuid(Buffer.from(uuidToBytes(ourAci)));
} }
async startCallingLobby({ async startCallingLobby({
@ -1006,11 +1006,16 @@ export class CallingClass {
public formatGroupCallPeekInfoForRedux( public formatGroupCallPeekInfoForRedux(
peekInfo: PeekInfo peekInfo: PeekInfo
): GroupCallPeekInfoType { ): GroupCallPeekInfoType {
const creatorAci = peekInfo.creator && bytesToUuid(peekInfo.creator);
return { return {
uuids: peekInfo.devices.map(peekDeviceInfo => { acis: peekInfo.devices.map(peekDeviceInfo => {
if (peekDeviceInfo.userId) { if (peekDeviceInfo.userId) {
const uuid = bytesToUuid(peekDeviceInfo.userId); const uuid = bytesToUuid(peekDeviceInfo.userId);
if (uuid) { if (uuid) {
assertDev(
isAciString(uuid),
'peeked participant uuid must be an ACI'
);
return uuid; return uuid;
} }
log.error( log.error(
@ -1021,9 +1026,18 @@ export class CallingClass {
'Calling.formatGroupCallPeekInfoForRedux: device had no user ID; using fallback UUID' 'Calling.formatGroupCallPeekInfoForRedux: device had no user ID; using fallback UUID'
); );
} }
return '00000000-0000-4000-8000-000000000000'; return normalizeAci(
'00000000-0000-4000-8000-000000000000',
'formatGrouPCallPeekInfoForRedux'
);
}), }),
creatorUuid: peekInfo.creator && bytesToUuid(peekInfo.creator), creatorAci:
creatorAci !== undefined
? normalizeAci(
creatorAci,
'formatGroupCallPeekInfoForRedux.creatorAci'
)
: undefined,
eraId: peekInfo.eraId, eraId: peekInfo.eraId,
maxDevices: peekInfo.maxDevices ?? Infinity, maxDevices: peekInfo.maxDevices ?? Infinity,
deviceCount: peekInfo.deviceCount, deviceCount: peekInfo.deviceCount,
@ -1060,15 +1074,16 @@ export class CallingClass {
? this.formatGroupCallPeekInfoForRedux(peekInfo) ? this.formatGroupCallPeekInfoForRedux(peekInfo)
: undefined, : undefined,
remoteParticipants: remoteDeviceStates.map(remoteDeviceState => { remoteParticipants: remoteDeviceStates.map(remoteDeviceState => {
let uuid = bytesToUuid(remoteDeviceState.userId); let aci = bytesToUuid(remoteDeviceState.userId);
if (!uuid) { if (!aci) {
log.error( log.error(
'Calling.formatGroupCallForRedux: could not convert remote participant UUID Uint8Array to string; using fallback UUID' 'Calling.formatGroupCallForRedux: could not convert remote participant UUID Uint8Array to string; using fallback UUID'
); );
uuid = '00000000-0000-4000-8000-000000000000'; aci = '00000000-0000-4000-8000-000000000000';
} }
assertDev(isAciString(aci), 'remote participant aci must be a aci');
return { return {
uuid, aci,
demuxId: remoteDeviceState.demuxId, demuxId: remoteDeviceState.demuxId,
hasRemoteAudio: !remoteDeviceState.audioMuted, hasRemoteAudio: !remoteDeviceState.audioMuted,
hasRemoteVideo: !remoteDeviceState.videoMuted, hasRemoteVideo: !remoteDeviceState.videoMuted,
@ -1610,7 +1625,7 @@ export class CallingClass {
return; return;
} }
const remoteUserId = envelope.sourceUuid; const remoteUserId = envelope.sourceServiceId;
const remoteDeviceId = this.parseDeviceId(envelope.sourceDevice); const remoteDeviceId = this.parseDeviceId(envelope.sourceDevice);
if (!remoteUserId || !remoteDeviceId || !this.localDeviceId) { if (!remoteUserId || !remoteDeviceId || !this.localDeviceId) {
log.error('Missing identifier, ignoring call message.'); log.error('Missing identifier, ignoring call message.');
@ -1620,16 +1635,16 @@ export class CallingClass {
const { storage } = window.textsecure; const { storage } = window.textsecure;
const senderIdentityRecord = const senderIdentityRecord =
await storage.protocol.getOrMigrateIdentityRecord(new UUID(remoteUserId)); await storage.protocol.getOrMigrateIdentityRecord(remoteUserId);
if (!senderIdentityRecord) { if (!senderIdentityRecord) {
log.error('Missing sender identity record; ignoring call message.'); log.error('Missing sender identity record; ignoring call message.');
return; return;
} }
const senderIdentityKey = senderIdentityRecord.publicKey.slice(1); // Ignore the type header, it is not used. const senderIdentityKey = senderIdentityRecord.publicKey.slice(1); // Ignore the type header, it is not used.
const ourUuid = storage.user.getCheckedUuid(); const ourAci = storage.user.getCheckedAci();
const receiverIdentityRecord = storage.protocol.getIdentityRecord(ourUuid); const receiverIdentityRecord = storage.protocol.getIdentityRecord(ourAci);
if (!receiverIdentityRecord) { if (!receiverIdentityRecord) {
log.error('Missing receiver identity record; ignoring call message.'); log.error('Missing receiver identity record; ignoring call message.');
return; return;
@ -1683,8 +1698,8 @@ export class CallingClass {
return; return;
} }
const sourceUuid = envelope.sourceUuid const sourceServiceId = envelope.sourceServiceId
? uuidToBytes(envelope.sourceUuid) ? uuidToBytes(envelope.sourceServiceId)
: null; : null;
const messageAgeSec = envelope.messageAgeSec ? envelope.messageAgeSec : 0; const messageAgeSec = envelope.messageAgeSec ? envelope.messageAgeSec : 0;
@ -1693,7 +1708,7 @@ export class CallingClass {
RingRTC.handleCallingMessage( RingRTC.handleCallingMessage(
remoteUserId, remoteUserId,
sourceUuid ? Buffer.from(sourceUuid) : null, sourceServiceId ? Buffer.from(sourceServiceId) : null,
remoteDeviceId, remoteDeviceId,
this.localDeviceId, this.localDeviceId,
messageAgeSec, messageAgeSec,
@ -1839,6 +1854,7 @@ export class CallingClass {
log.error('handleGroupCallRingUpdate(): ringerUuid was invalid'); log.error('handleGroupCallRingUpdate(): ringerUuid was invalid');
return; return;
} }
const ringerAci = normalizeAci(ringerUuid, 'handleGroupCallRingUpdate');
const conversation = window.ConversationController.get(groupId); const conversation = window.ConversationController.get(groupId);
if (!conversation) { if (!conversation) {
@ -1852,21 +1868,21 @@ export class CallingClass {
return; return;
} }
const ourACI = window.textsecure.storage.user.getCheckedUuid(UUIDKind.ACI); const ourAci = window.textsecure.storage.user.getCheckedAci();
if (conversation.get('left') || !conversation.hasMember(ourACI)) { if (conversation.get('left') || !conversation.hasMember(ourAci)) {
log.warn(`${logId}: we left the group`); log.warn(`${logId}: we left the group`);
return; return;
} }
if (!conversation.hasMember(new UUID(ringerUuid))) { if (!conversation.hasMember(ringerAci)) {
log.warn(`${logId}: they left the group`); log.warn(`${logId}: they left the group`);
return; return;
} }
if ( if (
conversation.get('announcementsOnly') && conversation.get('announcementsOnly') &&
!conversation.isAdmin(new UUID(ringerUuid)) !conversation.isAdmin(ringerAci)
) { ) {
log.warn(`${logId}: non-admin update to announcement-only group`); log.warn(`${logId}: non-admin update to announcement-only group`);
return; return;
@ -1897,7 +1913,7 @@ export class CallingClass {
this.reduxInterface?.receiveIncomingGroupCall({ this.reduxInterface?.receiveIncomingGroupCall({
conversationId, conversationId,
ringId, ringId,
ringerUuid, ringerAci,
}); });
} else { } else {
log.info('handleGroupCallRingUpdate: canceling the existing ring'); log.info('handleGroupCallRingUpdate: canceling the existing ring');
@ -1938,6 +1954,7 @@ export class CallingClass {
} }
try { try {
assertDev(isAciString(remoteUserId), 'remoteUserId is not a aci');
const result = await handleMessageSend( const result = await handleMessageSend(
window.textsecure.messaging.sendCallingMessage( window.textsecure.messaging.sendCallingMessage(
remoteUserId, remoteUserId,

View file

@ -5,7 +5,7 @@ import PQueue from 'p-queue';
import type { ContactSyncEvent } from '../textsecure/messageReceiverEvents'; import type { ContactSyncEvent } from '../textsecure/messageReceiverEvents';
import type { ModifiedContactDetails } from '../textsecure/ContactsParser'; import type { ModifiedContactDetails } from '../textsecure/ContactsParser';
import { UUID } from '../types/UUID'; import { normalizeServiceId } from '../types/ServiceId';
import * as Conversation from '../types/Conversation'; import * as Conversation from '../types/Conversation';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import type { ValidateConversationType } from '../model-types.d'; import type { ValidateConversationType } from '../model-types.d';
@ -91,7 +91,7 @@ async function doContactSync({
for (const details of contacts) { for (const details of contacts) {
const partialConversation: ValidateConversationType = { const partialConversation: ValidateConversationType = {
e164: details.number, e164: details.number,
uuid: UUID.cast(details.uuid), uuid: normalizeServiceId(details.aci, 'doContactSync'),
type: 'private', type: 'private',
}; };
@ -106,7 +106,7 @@ async function doContactSync({
const { conversation } = window.ConversationController.maybeMergeContacts({ const { conversation } = window.ConversationController.maybeMergeContacts({
e164: details.number, e164: details.number,
aci: details.uuid, aci: details.aci,
reason: logId, reason: logId,
}); });

View file

@ -22,7 +22,7 @@ export function getDistributionListsForRedux(): Array<StoryDistributionListDataT
id: list.id, id: list.id,
isBlockList: Boolean(list.isBlockList), isBlockList: Boolean(list.isBlockList),
name: list.name, name: list.name,
memberUuids: list.members, memberServiceIds: list.members,
})) }))
.filter(list => !list.deletedAtTimestamp); .filter(list => !list.deletedAtTimestamp);

View file

@ -12,7 +12,6 @@ import * as durations from '../util/durations';
import { BackOff } from '../util/BackOff'; import { BackOff } from '../util/BackOff';
import { sleep } from '../util/sleep'; import { sleep } from '../util/sleep';
import { toDayMillis } from '../util/timestamp'; import { toDayMillis } from '../util/timestamp';
import { UUIDKind } from '../types/UUID';
import * as log from '../logging/log'; import * as log from '../logging/log';
export const GROUP_CREDENTIALS_KEY = 'groupCredentials'; export const GROUP_CREDENTIALS_KEY = 'groupCredentials';
@ -121,7 +120,7 @@ export function getCheckedCredentialsForToday(
export async function maybeFetchNewCredentials(): Promise<void> { export async function maybeFetchNewCredentials(): Promise<void> {
const logId = 'maybeFetchNewCredentials'; const logId = 'maybeFetchNewCredentials';
const aci = window.textsecure.storage.user.getUuid(UUIDKind.ACI)?.toString(); const aci = window.textsecure.storage.user.getAci();
if (!aci) { if (!aci) {
log.info(`${logId}: no ACI, returning early`); log.info(`${logId}: no ACI, returning early`);
return; return;
@ -156,8 +155,8 @@ export async function maybeFetchNewCredentials(): Promise<void> {
); );
strictAssert(pni, 'Server must give pni along with group credentials'); strictAssert(pni, 'Server must give pni along with group credentials');
const localPni = window.storage.user.getUuid(UUIDKind.PNI); const localPni = window.storage.user.getPni();
if (pni !== localPni?.toString()) { if (pni !== localPni) {
log.error(`${logId}: local PNI ${localPni}, does not match remote ${pni}`); log.error(`${logId}: local PNI ${localPni}, does not match remote ${pni}`);
} }

View file

@ -10,7 +10,7 @@ import type {
GetProfileOptionsType, GetProfileOptionsType,
GetProfileUnauthOptionsType, GetProfileUnauthOptionsType,
} from '../textsecure/WebAPI'; } from '../textsecure/WebAPI';
import type { UUID } from '../types/UUID'; import type { ServiceIdString } from '../types/ServiceId';
import * as log from '../logging/log'; import * as log from '../logging/log';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import * as Bytes from '../Bytes'; import * as Bytes from '../Bytes';
@ -234,7 +234,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
const profileKey = c.get('profileKey'); const profileKey = c.get('profileKey');
const profileKeyVersion = c.deriveProfileKeyVersion(); const profileKeyVersion = c.deriveProfileKeyVersion();
const uuid = c.getCheckedUuid('getProfile'); const serviceId = c.getCheckedServiceId('getProfile');
const lastProfile = c.get('lastProfile'); const lastProfile = c.get('lastProfile');
let profileCredentialRequestContext: let profileCredentialRequestContext:
@ -268,7 +268,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
context: profileCredentialRequestContext, context: profileCredentialRequestContext,
} = generateProfileKeyCredentialRequest( } = generateProfileKeyCredentialRequest(
clientZkProfileCipher, clientZkProfileCipher,
uuid.toString(), serviceId,
profileKey profileKey
)); ));
@ -304,7 +304,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
try { try {
if (getProfileOptions.accessKey) { if (getProfileOptions.accessKey) {
try { try {
profile = await messaging.getProfile(uuid, getProfileOptions); profile = await messaging.getProfile(serviceId, getProfileOptions);
} catch (error) { } catch (error) {
if (!(error instanceof HTTPError)) { if (!(error instanceof HTTPError)) {
throw error; throw error;
@ -332,7 +332,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
// We won't get the credential, but lets either fetch: // We won't get the credential, but lets either fetch:
// - a versioned profile using last known profileKeyVersion // - a versioned profile using last known profileKeyVersion
// - some basic profile information (capabilities, badges, etc). // - some basic profile information (capabilities, badges, etc).
profile = await messaging.getProfile(uuid, getProfileOptions); profile = await messaging.getProfile(serviceId, getProfileOptions);
} catch (error) { } catch (error) {
if (error instanceof HTTPError && error.code === 404) { if (error instanceof HTTPError && error.code === 404) {
log.info(`getProfile: failed to find a profile for ${idForLogging}`); log.info(`getProfile: failed to find a profile for ${idForLogging}`);
@ -348,7 +348,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
} }
if (profile.identityKey) { if (profile.identityKey) {
await updateIdentityKey(profile.identityKey, uuid); await updateIdentityKey(profile.identityKey, serviceId);
} }
// Update accessKey to prevent race conditions. Since we run asynchronous // Update accessKey to prevent race conditions. Since we run asynchronous
@ -574,7 +574,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
export async function updateIdentityKey( export async function updateIdentityKey(
identityKey: string, identityKey: string,
uuid: UUID serviceId: ServiceIdString
): Promise<void> { ): Promise<void> {
if (!identityKey) { if (!identityKey) {
return; return;
@ -582,17 +582,17 @@ export async function updateIdentityKey(
const identityKeyBytes = Bytes.fromBase64(identityKey); const identityKeyBytes = Bytes.fromBase64(identityKey);
const changed = await window.textsecure.storage.protocol.saveIdentity( const changed = await window.textsecure.storage.protocol.saveIdentity(
new Address(uuid, 1), new Address(serviceId, 1),
identityKeyBytes, identityKeyBytes,
false false
); );
if (changed) { if (changed) {
log.info(`updateIdentityKey(${uuid.toString()}): changed`); log.info(`updateIdentityKey(${serviceId}): changed`);
// save identity will close all sessions except for .1, so we // save identity will close all sessions except for .1, so we
// must close that one manually. // must close that one manually.
const ourUuid = window.textsecure.storage.user.getCheckedUuid(); const ourAci = window.textsecure.storage.user.getCheckedAci();
await window.textsecure.storage.protocol.archiveSession( await window.textsecure.storage.protocol.archiveSession(
new QualifiedAddress(ourUuid, new Address(uuid, 1)) new QualifiedAddress(ourAci, new Address(serviceId, 1))
); );
} }
} }

View file

@ -1,25 +1,30 @@
// Copyright 2022 Signal Messenger, LLC // Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { v4 as generateUuid } from 'uuid';
import type { ExplodePromiseResultType } from '../util/explodePromise'; import type { ExplodePromiseResultType } from '../util/explodePromise';
import type { UUIDStringType } from '../types/UUID';
import { UUID } from '../types/UUID'; export type SingleServePromiseIdString = string & { __single_serve: never };
// This module provides single serve promises in a pub/sub manner. // This module provides single serve promises in a pub/sub manner.
// One example usage is if you're calling a redux action creator but need to // One example usage is if you're calling a redux action creator but need to
// await some result within it, you may pass in this promise and access it in // await some result within it, you may pass in this promise and access it in
// other parts of the app via its referencing UUID. // other parts of the app via its referencing UUID.
// eslint-disable-next-line @typescript-eslint/no-explicit-any const promises = new Map<
const promises = new Map<UUIDStringType, ExplodePromiseResultType<any>>(); SingleServePromiseIdString,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ExplodePromiseResultType<any>
>();
export function set<T>( export function set<T>(
explodedPromise: ExplodePromiseResultType<T> explodedPromise: ExplodePromiseResultType<T>
): UUIDStringType { ): SingleServePromiseIdString {
let uuid = UUID.generate().toString(); let uuid = generateUuid() as SingleServePromiseIdString;
while (promises.has(uuid)) { while (promises.has(uuid)) {
uuid = UUID.generate().toString(); uuid = generateUuid() as SingleServePromiseIdString;
} }
promises.set(uuid, { promises.set(uuid, {
@ -38,7 +43,7 @@ export function set<T>(
} }
export function get<T>( export function get<T>(
uuid: UUIDStringType uuid: SingleServePromiseIdString
): ExplodePromiseResultType<T> | undefined { ): ExplodePromiseResultType<T> | undefined {
return promises.get(uuid); return promises.get(uuid);
} }

View file

@ -1512,7 +1512,7 @@ async function processRemoteRecords(
// Find remote contact records that: // Find remote contact records that:
// - Have `remote.pni === remote.serviceUuid` and have `remote.serviceE164` // - Have `remote.pni === remote.serviceUuid` and have `remote.serviceE164`
// - Match local contact that has `local.serviceUuid != remote.pni`. // - Match local contact that has `aci`.
const splitPNIContacts = new Array<MergeableItemType>(); const splitPNIContacts = new Array<MergeableItemType>();
prunedStorageItems = prunedStorageItems.filter(item => { prunedStorageItems = prunedStorageItems.filter(item => {
const { itemType, storageRecord } = item; const { itemType, storageRecord } = item;
@ -1521,18 +1521,12 @@ async function processRemoteRecords(
return true; return true;
} }
if ( if (!contact.serviceE164 || !contact.pni) {
!contact.serviceE164 ||
!contact.pni ||
contact.pni !== contact.serviceUuid
) {
return true; return true;
} }
const localUuid = window.ConversationController.get(contact.pni)?.get( const localAci = window.ConversationController.get(contact.pni)?.getAci();
'uuid' if (!localAci) {
);
if (!localUuid || localUuid === contact.pni) {
return true; return true;
} }

View file

@ -12,9 +12,8 @@ import {
waitThenMaybeUpdateGroup, waitThenMaybeUpdateGroup,
waitThenRespondToGroupV2Migration, waitThenRespondToGroupV2Migration,
} from '../groups'; } from '../groups';
import { assertDev } from '../util/assert'; import { assertDev, strictAssert } from '../util/assert';
import { dropNull } from '../util/dropNull'; import { dropNull } from '../util/dropNull';
import { normalizeUuid } from '../util/normalizeUuid';
import { missingCaseError } from '../util/missingCaseError'; import { missingCaseError } from '../util/missingCaseError';
import { isNotNil } from '../util/isNotNil'; import { isNotNil } from '../util/isNotNil';
import { import {
@ -39,11 +38,18 @@ import {
import { ourProfileKeyService } from './ourProfileKey'; import { ourProfileKeyService } from './ourProfileKey';
import { isGroupV1, isGroupV2 } from '../util/whatTypeOfConversation'; import { isGroupV1, isGroupV2 } from '../util/whatTypeOfConversation';
import { DurationInSeconds } from '../util/durations'; import { DurationInSeconds } from '../util/durations';
import { isValidUuid, UUID, UUIDKind } from '../types/UUID';
import * as preferredReactionEmoji from '../reactions/preferredReactionEmoji'; import * as preferredReactionEmoji from '../reactions/preferredReactionEmoji';
import { SignalService as Proto } from '../protobuf'; import { SignalService as Proto } from '../protobuf';
import * as log from '../logging/log'; import * as log from '../logging/log';
import type { UUIDStringType } from '../types/UUID'; import { normalizeStoryDistributionId } from '../types/StoryDistributionId';
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import type { ServiceIdString } from '../types/ServiceId';
import {
normalizeServiceId,
normalizeAci,
normalizePni,
ServiceIdKind,
} from '../types/ServiceId';
import * as Stickers from '../types/Stickers'; import * as Stickers from '../types/Stickers';
import type { import type {
StoryDistributionWithMembersType, StoryDistributionWithMembersType,
@ -147,9 +153,9 @@ export async function toContactRecord(
conversation: ConversationModel conversation: ConversationModel
): Promise<Proto.ContactRecord> { ): Promise<Proto.ContactRecord> {
const contactRecord = new Proto.ContactRecord(); const contactRecord = new Proto.ContactRecord();
const uuid = conversation.getUuid(); const aci = conversation.getAci();
if (uuid) { if (aci) {
contactRecord.serviceUuid = uuid.toString(); contactRecord.aci = aci;
} }
const e164 = conversation.get('e164'); const e164 = conversation.get('e164');
if (e164) { if (e164) {
@ -160,7 +166,7 @@ export async function toContactRecord(
if (username && canHaveUsername(conversation.attributes, ourID)) { if (username && canHaveUsername(conversation.attributes, ourID)) {
contactRecord.username = username; contactRecord.username = username;
} }
const pni = conversation.get('pni'); const pni = conversation.getPni();
if (pni && RemoteConfig.isEnabled('desktop.pnp')) { if (pni && RemoteConfig.isEnabled('desktop.pnp')) {
contactRecord.pni = pni; contactRecord.pni = pni;
} }
@ -169,8 +175,9 @@ export async function toContactRecord(
contactRecord.profileKey = Bytes.fromBase64(String(profileKey)); contactRecord.profileKey = Bytes.fromBase64(String(profileKey));
} }
const identityKey = uuid const serviceId = aci ?? pni;
? await window.textsecure.storage.protocol.loadIdentityKey(uuid) const identityKey = serviceId
? await window.textsecure.storage.protocol.loadIdentityKey(serviceId)
: undefined; : undefined;
if (identityKey) { if (identityKey) {
contactRecord.identityKey = identityKey; contactRecord.identityKey = identityKey;
@ -510,7 +517,8 @@ export function toStoryDistributionListRecord(
storyDistributionListRecord.isBlockList = Boolean( storyDistributionListRecord.isBlockList = Boolean(
storyDistributionList.isBlockList storyDistributionList.isBlockList
); );
storyDistributionListRecord.recipientUuids = storyDistributionList.members; storyDistributionListRecord.recipientServiceIds =
storyDistributionList.members;
if (storyDistributionList.storageUnknownFields) { if (storyDistributionList.storageUnknownFields) {
storyDistributionListRecord.$unknownFields = [ storyDistributionListRecord.$unknownFields = [
@ -634,12 +642,8 @@ function doRecordsConflict(
// false, empty string, or 0 for these records we do not count them as // false, empty string, or 0 for these records we do not count them as
// conflicting. // conflicting.
if ( if (
// eslint-disable-next-line eqeqeq (!remoteValue || (Long.isLong(remoteValue) && remoteValue.isZero())) &&
remoteValue === null && (!localValue || (Long.isLong(localValue) && localValue.isZero()))
(localValue === false ||
localValue === '' ||
localValue === 0 ||
(Long.isLong(localValue) && localValue.toNumber() === 0))
) { ) {
continue; continue;
} }
@ -962,46 +966,45 @@ export async function mergeContactRecord(
const contactRecord = { const contactRecord = {
...originalContactRecord, ...originalContactRecord,
serviceUuid: originalContactRecord.serviceUuid aci: originalContactRecord.aci
? normalizeUuid( ? normalizeAci(originalContactRecord.aci, 'ContactRecord.aci')
originalContactRecord.serviceUuid, : undefined,
'ContactRecord.serviceUuid' pni: originalContactRecord.pni
) ? normalizePni(originalContactRecord.pni, 'ContactRecord.pni')
: undefined, : undefined,
}; };
const isPniSupported = RemoteConfig.isEnabled('desktop.pnp'); const isPniSupported = RemoteConfig.isEnabled('desktop.pnp');
const e164 = dropNull(contactRecord.serviceE164); const e164 = dropNull(contactRecord.serviceE164);
const uuid = dropNull(contactRecord.serviceUuid); const { aci } = contactRecord;
const pni = isPniSupported ? dropNull(contactRecord.pni) : undefined; const pni = isPniSupported ? dropNull(contactRecord.pni) : undefined;
const serviceId = aci || pni;
// All contacts must have UUID // All contacts must have UUID
if (!uuid) { if (!serviceId) {
return { hasConflict: false, shouldDrop: true, details: ['no uuid'] }; return { hasConflict: false, shouldDrop: true, details: ['no uuid'] };
} }
if (!isValidUuid(uuid)) { if (
return { hasConflict: false, shouldDrop: true, details: ['invalid uuid'] }; window.storage.user.getOurServiceIdKind(serviceId) !== ServiceIdKind.Unknown
} ) {
if (window.storage.user.getOurUuidKind(new UUID(uuid)) !== UUIDKind.Unknown) {
return { hasConflict: false, shouldDrop: true, details: ['our own uuid'] }; return { hasConflict: false, shouldDrop: true, details: ['our own uuid'] };
} }
const { conversation } = window.ConversationController.maybeMergeContacts({ const { conversation } = window.ConversationController.maybeMergeContacts({
aci: uuid, aci,
e164, e164,
pni, pni,
reason: 'mergeContactRecord', reason: 'mergeContactRecord',
}); });
// We're going to ignore this; it's likely a PNI-only contact we've already merged // We're going to ignore this; it's likely a PNI-only contact we've already merged
if (conversation.get('uuid') !== uuid) { if (conversation.get('uuid') !== serviceId) {
log.warn( log.warn(
`mergeContactRecord: ${conversation.idForLogging()} ` + `mergeContactRecord: ${conversation.idForLogging()} ` +
`with storageId ${conversation.get('storageID')} ` + `with storageId ${conversation.get('storageID')} ` +
`had uuid that didn't match provided uuid ${uuid}` `had serviceId that didn't match provided serviceId ${serviceId}`
); );
return { return {
hasConflict: false, hasConflict: false,
@ -1061,7 +1064,7 @@ export async function mergeContactRecord(
const needsNotification = const needsNotification =
await window.textsecure.storage.protocol.updateIdentityAfterSync( await window.textsecure.storage.protocol.updateIdentityAfterSync(
new UUID(uuid), serviceId,
newVerified, newVerified,
contactRecord.identityKey contactRecord.identityKey
); );
@ -1526,20 +1529,24 @@ export async function mergeStoryDistributionListRecord(
storyDistributionListRecord.identifier storyDistributionListRecord.identifier
); );
const listId = isMyStory let listId: StoryDistributionIdString;
? MY_STORY_ID if (isMyStory) {
: bytesToUuid(storyDistributionListRecord.identifier); listId = MY_STORY_ID;
} else {
if (!listId) { const uuid = bytesToUuid(storyDistributionListRecord.identifier);
throw new Error('Could not parse distribution list id'); strictAssert(uuid, 'mergeStoryDistributionListRecord: no distribution id');
listId = normalizeStoryDistributionId(
uuid,
'mergeStoryDistributionListRecord'
);
} }
const localStoryDistributionList = const localStoryDistributionList =
await dataInterface.getStoryDistributionWithMembers(listId); await dataInterface.getStoryDistributionWithMembers(listId);
const remoteListMembers: Array<UUIDStringType> = ( const remoteListMembers: Array<ServiceIdString> = (
storyDistributionListRecord.recipientUuids || [] storyDistributionListRecord.recipientServiceIds || []
).map(UUID.cast); ).map(id => normalizeServiceId(id, 'mergeStoryDistributionListRecord'));
if (storyDistributionListRecord.$unknownFields) { if (storyDistributionListRecord.$unknownFields) {
details.push('adding unknown fields'); details.push('adding unknown fields');
@ -1608,12 +1615,12 @@ export async function mergeStoryDistributionListRecord(
); );
const localMembersListSet = new Set(localStoryDistributionList.members); const localMembersListSet = new Set(localStoryDistributionList.members);
const toAdd: Array<UUIDStringType> = remoteListMembers.filter( const toAdd: Array<ServiceIdString> = remoteListMembers.filter(
uuid => !localMembersListSet.has(uuid) uuid => !localMembersListSet.has(uuid)
); );
const remoteMemberListSet = new Set(remoteListMembers); const remoteMemberListSet = new Set(remoteListMembers);
const toRemove: Array<UUIDStringType> = const toRemove: Array<ServiceIdString> =
localStoryDistributionList.members.filter( localStoryDistributionList.members.filter(
uuid => !remoteMemberListSet.has(uuid) uuid => !remoteMemberListSet.has(uuid)
); );

View file

@ -172,7 +172,7 @@ async function repairUnexpiredStories(): Promise<void> {
await Promise.all( await Promise.all(
storiesWithExpiry.map(messageAttributes => { storiesWithExpiry.map(messageAttributes => {
return window.Signal.Data.saveMessage(messageAttributes, { return window.Signal.Data.saveMessage(messageAttributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(), ourAci: window.textsecure.storage.user.getCheckedAci(),
}); });
}) })
); );

View file

@ -24,7 +24,6 @@ import { createStore } from './state/createStore';
import * as TypesAttachment from './types/Attachment'; import * as TypesAttachment from './types/Attachment';
import * as VisualAttachment from './types/VisualAttachment'; import * as VisualAttachment from './types/VisualAttachment';
import * as MessageType from './types/Message2'; import * as MessageType from './types/Message2';
import { UUID } from './types/UUID';
import { Address } from './types/Address'; import { Address } from './types/Address';
import { QualifiedAddress } from './types/QualifiedAddress'; import { QualifiedAddress } from './types/QualifiedAddress';
@ -388,7 +387,6 @@ export const setup = (options: {
Message: MessageType, Message: MessageType,
// Mostly for debugging // Mostly for debugging
UUID,
Address, Address,
QualifiedAddress, QualifiedAddress,
}; };

View file

@ -15,10 +15,10 @@ import { assertDev, softAssert } from '../util/assert';
import { mapObjectWithSpec } from '../util/mapObjectWithSpec'; import { mapObjectWithSpec } from '../util/mapObjectWithSpec';
import type { ObjectMappingSpecType } from '../util/mapObjectWithSpec'; import type { ObjectMappingSpecType } from '../util/mapObjectWithSpec';
import { cleanDataForIpc } from './cleanDataForIpc'; import { cleanDataForIpc } from './cleanDataForIpc';
import type { UUIDStringType } from '../types/UUID'; import type { AciString } from '../types/ServiceId';
import createTaskWithTimeout from '../textsecure/TaskWithTimeout'; import createTaskWithTimeout from '../textsecure/TaskWithTimeout';
import * as log from '../logging/log'; import * as log from '../logging/log';
import { isValidUuid } from '../types/UUID'; import { isValidUuid } from '../util/isValidUuid';
import * as Errors from '../types/errors'; import * as Errors from '../types/errors';
import type { StoredJob } from '../jobs/types'; import type { StoredJob } from '../jobs/types';
@ -539,7 +539,7 @@ async function saveMessage(
options: { options: {
jobToInsert?: Readonly<StoredJob>; jobToInsert?: Readonly<StoredJob>;
forceSave?: boolean; forceSave?: boolean;
ourUuid: UUIDStringType; ourAci: AciString;
} }
): Promise<string> { ): Promise<string> {
const id = await channels.saveMessage(_cleanMessageData(data), { const id = await channels.saveMessage(_cleanMessageData(data), {
@ -557,7 +557,7 @@ async function saveMessage(
async function saveMessages( async function saveMessages(
arrayOfMessages: ReadonlyArray<MessageType>, arrayOfMessages: ReadonlyArray<MessageType>,
options: { forceSave?: boolean; ourUuid: UUIDStringType } options: { forceSave?: boolean; ourAci: AciString }
): Promise<void> { ): Promise<void> {
await channels.saveMessages( await channels.saveMessages(
arrayOfMessages.map(message => _cleanMessageData(message)), arrayOfMessages.map(message => _cleanMessageData(message)),

View file

@ -13,7 +13,8 @@ import type { StorageAccessType } from '../types/Storage.d';
import type { AttachmentType } from '../types/Attachment'; import type { AttachmentType } from '../types/Attachment';
import type { BytesToStrings } from '../types/Util'; import type { BytesToStrings } from '../types/Util';
import type { QualifiedAddressStringType } from '../types/QualifiedAddress'; import type { QualifiedAddressStringType } from '../types/QualifiedAddress';
import type { UUIDStringType } from '../types/UUID'; import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import type { AciString, ServiceIdString } from '../types/ServiceId';
import type { BadgeType } from '../badges/types'; import type { BadgeType } from '../badges/types';
import type { RemoveAllConfiguration } from '../types/RemoveAllConfiguration'; import type { RemoveAllConfiguration } from '../types/RemoveAllConfiguration';
import type { LoggerType } from '../types/Logging'; import type { LoggerType } from '../types/Logging';
@ -84,7 +85,7 @@ export type EmojiType = {
export type IdentityKeyType = { export type IdentityKeyType = {
firstUse: boolean; firstUse: boolean;
id: UUIDStringType | `conversation:${string}`; id: ServiceIdString | `conversation:${string}`;
nonblockingApproval: boolean; nonblockingApproval: boolean;
publicKey: Uint8Array; publicKey: Uint8Array;
timestamp: number; timestamp: number;
@ -92,7 +93,7 @@ export type IdentityKeyType = {
}; };
export type StoredIdentityKeyType = { export type StoredIdentityKeyType = {
firstUse: boolean; firstUse: boolean;
id: UUIDStringType | `conversation:${string}`; id: ServiceIdString | `conversation:${string}`;
nonblockingApproval: boolean; nonblockingApproval: boolean;
publicKey: string; publicKey: string;
timestamp: number; timestamp: number;
@ -116,7 +117,7 @@ export type MessageTypeUnhydrated = {
json: string; json: string;
}; };
export type PreKeyIdType = `${UUIDStringType}:${number}`; export type PreKeyIdType = `${ServiceIdString}:${number}`;
export type KyberPreKeyType = { export type KyberPreKeyType = {
id: PreKeyIdType; id: PreKeyIdType;
@ -125,7 +126,7 @@ export type KyberPreKeyType = {
isConfirmed: boolean; isConfirmed: boolean;
isLastResort: boolean; isLastResort: boolean;
keyId: number; keyId: number;
ourUuid: UUIDStringType; ourUuid: ServiceIdString;
}; };
export type StoredKyberPreKeyType = KyberPreKeyType & { export type StoredKyberPreKeyType = KyberPreKeyType & {
data: string; data: string;
@ -135,7 +136,7 @@ export type PreKeyType = {
createdAt: number; createdAt: number;
keyId: number; keyId: number;
ourUuid: UUIDStringType; ourUuid: ServiceIdString;
privateKey: Uint8Array; privateKey: Uint8Array;
publicKey: Uint8Array; publicKey: Uint8Array;
}; };
@ -171,7 +172,7 @@ export type SentProtoType = {
export type SentProtoWithMessageIdsType = SentProtoType & { export type SentProtoWithMessageIdsType = SentProtoType & {
messageIds: Array<string>; messageIds: Array<string>;
}; };
export type SentRecipientsType = Record<string, Array<number>>; export type SentRecipientsType = Record<ServiceIdString, Array<number>>;
export type SentMessagesType = Array<string>; export type SentMessagesType = Array<string>;
// These two are for test only // These two are for test only
@ -198,8 +199,8 @@ export type SenderKeyType = {
export type SenderKeyIdType = SenderKeyType['id']; export type SenderKeyIdType = SenderKeyType['id'];
export type SessionType = { export type SessionType = {
id: QualifiedAddressStringType; id: QualifiedAddressStringType;
ourUuid: UUIDStringType; ourUuid: ServiceIdString;
uuid: UUIDStringType; uuid: ServiceIdString;
conversationId: string; conversationId: string;
deviceId: number; deviceId: number;
record: string; record: string;
@ -209,8 +210,8 @@ export type SessionIdType = SessionType['id'];
export type SignedPreKeyType = { export type SignedPreKeyType = {
confirmed: boolean; confirmed: boolean;
created_at: number; created_at: number;
ourUuid: UUIDStringType; ourUuid: ServiceIdString;
id: `${UUIDStringType}:${number}`; id: `${ServiceIdString}:${number}`;
keyId: number; keyId: number;
privateKey: Uint8Array; privateKey: Uint8Array;
publicKey: Uint8Array; publicKey: Uint8Array;
@ -218,8 +219,8 @@ export type SignedPreKeyType = {
export type StoredSignedPreKeyType = { export type StoredSignedPreKeyType = {
confirmed: boolean; confirmed: boolean;
created_at: number; created_at: number;
ourUuid: UUIDStringType; ourUuid: ServiceIdString;
id: `${UUIDStringType}:${number}`; id: `${ServiceIdString}:${number}`;
keyId: number; keyId: number;
privateKey: string; privateKey: string;
publicKey: string; publicKey: string;
@ -304,10 +305,10 @@ export type UnprocessedType = {
messageAgeSec?: number; messageAgeSec?: number;
source?: string; source?: string;
sourceUuid?: UUIDStringType; sourceUuid?: ServiceIdString;
sourceDevice?: number; sourceDevice?: number;
destinationUuid?: string; destinationUuid?: ServiceIdString;
updatedPni?: string; updatedPni?: ServiceIdString;
serverGuid?: string; serverGuid?: string;
serverTimestamp?: number; serverTimestamp?: number;
decrypted?: string; decrypted?: string;
@ -318,7 +319,7 @@ export type UnprocessedType = {
export type UnprocessedUpdateType = { export type UnprocessedUpdateType = {
source?: string; source?: string;
sourceUuid?: UUIDStringType; sourceUuid?: ServiceIdString;
sourceDevice?: number; sourceDevice?: number;
serverGuid?: string; serverGuid?: string;
serverTimestamp?: number; serverTimestamp?: number;
@ -333,7 +334,7 @@ export type ConversationMessageStatsType = {
export type DeleteSentProtoRecipientOptionsType = Readonly<{ export type DeleteSentProtoRecipientOptionsType = Readonly<{
timestamp: number; timestamp: number;
recipientUuid: string; recipientServiceId: ServiceIdString;
deviceId: number; deviceId: number;
}>; }>;
@ -342,7 +343,7 @@ export type DeleteSentProtoRecipientResultType = Readonly<{
}>; }>;
export type StoryDistributionType = Readonly<{ export type StoryDistributionType = Readonly<{
id: UUIDStringType; id: StoryDistributionIdString;
name: string; name: string;
deletedAtTimestamp?: number; deletedAtTimestamp?: number;
allowsReplies: boolean; allowsReplies: boolean;
@ -351,17 +352,17 @@ export type StoryDistributionType = Readonly<{
}> & }> &
StorageServiceFieldsType; StorageServiceFieldsType;
export type StoryDistributionMemberType = Readonly<{ export type StoryDistributionMemberType = Readonly<{
listId: UUIDStringType; listId: StoryDistributionIdString;
uuid: UUIDStringType; uuid: ServiceIdString;
}>; }>;
export type StoryDistributionWithMembersType = Readonly< export type StoryDistributionWithMembersType = Readonly<
{ {
members: Array<UUIDStringType>; members: Array<ServiceIdString>;
} & StoryDistributionType } & StoryDistributionType
>; >;
export type StoryReadType = Readonly<{ export type StoryReadType = Readonly<{
authorId: UUIDStringType; authorId: ServiceIdString;
conversationId: string; conversationId: string;
storyId: string; storyId: string;
storyReadDate: number; storyReadDate: number;
@ -433,17 +434,17 @@ export type DataInterface = {
removeKyberPreKeyById: ( removeKyberPreKeyById: (
id: PreKeyIdType | Array<PreKeyIdType> id: PreKeyIdType | Array<PreKeyIdType>
) => Promise<void>; ) => Promise<void>;
removeKyberPreKeysByUuid: (uuid: UUIDStringType) => Promise<void>; removeKyberPreKeysByServiceId: (serviceId: ServiceIdString) => Promise<void>;
removeAllKyberPreKeys: () => Promise<void>; removeAllKyberPreKeys: () => Promise<void>;
removePreKeyById: (id: PreKeyIdType | Array<PreKeyIdType>) => Promise<void>; removePreKeyById: (id: PreKeyIdType | Array<PreKeyIdType>) => Promise<void>;
removePreKeysByUuid: (uuid: UUIDStringType) => Promise<void>; removePreKeysByServiceId: (serviceId: ServiceIdString) => Promise<void>;
removeAllPreKeys: () => Promise<void>; removeAllPreKeys: () => Promise<void>;
removeSignedPreKeyById: ( removeSignedPreKeyById: (
id: SignedPreKeyIdType | Array<SignedPreKeyIdType> id: SignedPreKeyIdType | Array<SignedPreKeyIdType>
) => Promise<void>; ) => Promise<void>;
removeSignedPreKeysByUuid: (uuid: UUIDStringType) => Promise<void>; removeSignedPreKeysByServiceId: (serviceId: ServiceIdString) => Promise<void>;
removeAllSignedPreKeys: () => Promise<void>; removeAllSignedPreKeys: () => Promise<void>;
removeAllItems: () => Promise<void>; removeAllItems: () => Promise<void>;
@ -466,7 +467,7 @@ export type DataInterface = {
deleteSentProtoByMessageId: (messageId: string) => Promise<void>; deleteSentProtoByMessageId: (messageId: string) => Promise<void>;
insertProtoRecipients: (options: { insertProtoRecipients: (options: {
id: number; id: number;
recipientUuid: string; recipientServiceId: ServiceIdString;
deviceIds: Array<number>; deviceIds: Array<number>;
}) => Promise<void>; }) => Promise<void>;
deleteSentProtoRecipient: ( deleteSentProtoRecipient: (
@ -476,7 +477,7 @@ export type DataInterface = {
) => Promise<DeleteSentProtoRecipientResultType>; ) => Promise<DeleteSentProtoRecipientResultType>;
getSentProtoByRecipient: (options: { getSentProtoByRecipient: (options: {
now: number; now: number;
recipientUuid: string; recipientServiceId: ServiceIdString;
timestamp: number; timestamp: number;
}) => Promise<SentProtoWithMessageIdsType | undefined>; }) => Promise<SentProtoWithMessageIdsType | undefined>;
removeAllSentProtos: () => Promise<void>; removeAllSentProtos: () => Promise<void>;
@ -495,7 +496,7 @@ export type DataInterface = {
bulkAddSessions: (array: Array<SessionType>) => Promise<void>; bulkAddSessions: (array: Array<SessionType>) => Promise<void>;
removeSessionById: (id: SessionIdType) => Promise<void>; removeSessionById: (id: SessionIdType) => Promise<void>;
removeSessionsByConversation: (conversationId: string) => Promise<void>; removeSessionsByConversation: (conversationId: string) => Promise<void>;
removeSessionsByUUID: (uuid: UUIDStringType) => Promise<void>; removeSessionsByServiceId: (serviceId: ServiceIdString) => Promise<void>;
removeAllSessions: () => Promise<void>; removeAllSessions: () => Promise<void>;
getAllSessions: () => Promise<Array<SessionType>>; getAllSessions: () => Promise<Array<SessionType>>;
@ -519,8 +520,8 @@ export type DataInterface = {
getAllConversations: () => Promise<Array<ConversationType>>; getAllConversations: () => Promise<Array<ConversationType>>;
getAllConversationIds: () => Promise<Array<string>>; getAllConversationIds: () => Promise<Array<string>>;
getAllGroupsInvolvingUuid: ( getAllGroupsInvolvingServiceId: (
id: UUIDStringType serviceId: ServiceIdString
) => Promise<Array<ConversationType>>; ) => Promise<Array<ConversationType>>;
getMessageCount: (conversationId?: string) => Promise<number>; getMessageCount: (conversationId?: string) => Promise<number>;
@ -530,12 +531,12 @@ export type DataInterface = {
options: { options: {
jobToInsert?: StoredJob; jobToInsert?: StoredJob;
forceSave?: boolean; forceSave?: boolean;
ourUuid: UUIDStringType; ourAci: AciString;
} }
) => Promise<string>; ) => Promise<string>;
saveMessages: ( saveMessages: (
arrayOfMessages: ReadonlyArray<MessageType>, arrayOfMessages: ReadonlyArray<MessageType>,
options: { forceSave?: boolean; ourUuid: UUIDStringType } options: { forceSave?: boolean; ourAci: AciString }
) => Promise<void>; ) => Promise<void>;
removeMessage: (id: string) => Promise<void>; removeMessage: (id: string) => Promise<void>;
removeMessages: (ids: ReadonlyArray<string>) => Promise<void>; removeMessages: (ids: ReadonlyArray<string>) => Promise<void>;
@ -578,13 +579,13 @@ export type DataInterface = {
storyId?: string; storyId?: string;
}) => Promise<Array<ReactionResultType>>; }) => Promise<Array<ReactionResultType>>;
markReactionAsRead: ( markReactionAsRead: (
targetAuthorUuid: string, targetAuthorServiceId: ServiceIdString,
targetTimestamp: number targetTimestamp: number
) => Promise<ReactionType | undefined>; ) => Promise<ReactionType | undefined>;
removeReactionFromConversation: (reaction: { removeReactionFromConversation: (reaction: {
emoji: string; emoji: string;
fromId: string; fromId: string;
targetAuthorUuid: string; targetAuthorServiceId: ServiceIdString;
targetTimestamp: number; targetTimestamp: number;
}) => Promise<void>; }) => Promise<void>;
addReaction: (reactionObj: ReactionType) => Promise<void>; addReaction: (reactionObj: ReactionType) => Promise<void>;
@ -592,7 +593,7 @@ export type DataInterface = {
_removeAllReactions: () => Promise<void>; _removeAllReactions: () => Promise<void>;
getMessageBySender: (options: { getMessageBySender: (options: {
source?: string; source?: string;
sourceUuid?: UUIDStringType; sourceUuid?: ServiceIdString;
sourceDevice?: number; sourceDevice?: number;
sent_at: number; sent_at: number;
}) => Promise<MessageType | undefined>; }) => Promise<MessageType | undefined>;
@ -617,7 +618,7 @@ export type DataInterface = {
// getOlderMessagesByConversation is JSON on server, full message on Client // getOlderMessagesByConversation is JSON on server, full message on Client
getAllStories: (options: { getAllStories: (options: {
conversationId?: string; conversationId?: string;
sourceUuid?: UUIDStringType; sourceUuid?: ServiceIdString;
}) => Promise<GetAllStoriesResultType>; }) => Promise<GetAllStoriesResultType>;
// getNewerMessagesByConversation is JSON on server, full message on Client // getNewerMessagesByConversation is JSON on server, full message on Client
getMessageMetricsForConversation: (options: { getMessageMetricsForConversation: (options: {
@ -629,7 +630,6 @@ export type DataInterface = {
getConversationMessageStats: (options: { getConversationMessageStats: (options: {
conversationId: string; conversationId: string;
includeStoryReplies: boolean; includeStoryReplies: boolean;
ourUuid: UUIDStringType;
}) => Promise<ConversationMessageStatsType>; }) => Promise<ConversationMessageStatsType>;
getLastConversationMessage(options: { getLastConversationMessage(options: {
conversationId: string; conversationId: string;
@ -642,7 +642,7 @@ export type DataInterface = {
}): Promise<MessageType | undefined>; }): Promise<MessageType | undefined>;
getCallHistory( getCallHistory(
callId: string, callId: string,
peerId: string peerId: ServiceIdString | string
): Promise<CallHistoryDetails | undefined>; ): Promise<CallHistoryDetails | undefined>;
getCallHistoryGroupsCount(filter: CallHistoryFilter): Promise<number>; getCallHistoryGroupsCount(filter: CallHistoryFilter): Promise<number>;
getCallHistoryGroups( getCallHistoryGroups(
@ -667,7 +667,7 @@ export type DataInterface = {
) => Promise<string | null>; ) => Promise<string | null>;
saveEditedMessage: ( saveEditedMessage: (
mainMessage: MessageType, mainMessage: MessageType,
ourUuid: UUIDStringType, ourAci: AciString,
opts: EditedMessageType opts: EditedMessageType
) => Promise<void>; ) => Promise<void>;
getUnprocessedCount: () => Promise<number>; getUnprocessedCount: () => Promise<number>;
@ -767,25 +767,25 @@ export type DataInterface = {
modifyStoryDistributionMembers( modifyStoryDistributionMembers(
listId: string, listId: string,
options: { options: {
toAdd: Array<UUIDStringType>; toAdd: Array<ServiceIdString>;
toRemove: Array<UUIDStringType>; toRemove: Array<ServiceIdString>;
} }
): Promise<void>; ): Promise<void>;
modifyStoryDistributionWithMembers( modifyStoryDistributionWithMembers(
distribution: StoryDistributionType, distribution: StoryDistributionType,
options: { options: {
toAdd: Array<UUIDStringType>; toAdd: Array<ServiceIdString>;
toRemove: Array<UUIDStringType>; toRemove: Array<ServiceIdString>;
} }
): Promise<void>; ): Promise<void>;
deleteStoryDistribution(id: UUIDStringType): Promise<void>; deleteStoryDistribution(id: StoryDistributionIdString): Promise<void>;
_getAllStoryReads(): Promise<Array<StoryReadType>>; _getAllStoryReads(): Promise<Array<StoryReadType>>;
_deleteAllStoryReads(): Promise<void>; _deleteAllStoryReads(): Promise<void>;
addNewStoryRead(read: StoryReadType): Promise<void>; addNewStoryRead(read: StoryReadType): Promise<void>;
getLastStoryReadsForAuthor(options: { getLastStoryReadsForAuthor(options: {
authorId: UUIDStringType; authorId: ServiceIdString;
conversationId?: UUIDStringType; conversationId?: string;
limit?: number; limit?: number;
}): Promise<Array<StoryReadType>>; }): Promise<Array<StoryReadType>>;
countStoryReadsByConversation(conversationId: string): Promise<number>; countStoryReadsByConversation(conversationId: string): Promise<number>;

View file

@ -10,6 +10,7 @@ import { randomBytes } from 'crypto';
import type { Database, Statement } from '@signalapp/better-sqlite3'; import type { Database, Statement } from '@signalapp/better-sqlite3';
import SQL from '@signalapp/better-sqlite3'; import SQL from '@signalapp/better-sqlite3';
import pProps from 'p-props'; import pProps from 'p-props';
import { v4 as generateUuid } from 'uuid';
import { z } from 'zod'; import { z } from 'zod';
import type { Dictionary } from 'lodash'; import type { Dictionary } from 'lodash';
@ -33,8 +34,9 @@ import { ReadStatus } from '../messages/MessageReadStatus';
import type { GroupV2MemberType } from '../model-types.d'; import type { GroupV2MemberType } from '../model-types.d';
import type { ReactionType } from '../types/Reactions'; import type { ReactionType } from '../types/Reactions';
import { STORAGE_UI_KEYS } from '../types/StorageUIKeys'; import { STORAGE_UI_KEYS } from '../types/StorageUIKeys';
import { UUID } from '../types/UUID'; import type { StoryDistributionIdString } from '../types/StoryDistributionId';
import type { UUIDStringType } from '../types/UUID'; import type { ServiceIdString, AciString } from '../types/ServiceId';
import { isServiceIdString } from '../types/ServiceId';
import type { StoredJob } from '../jobs/types'; import type { StoredJob } from '../jobs/types';
import { assertDev, assertSync, strictAssert } from '../util/assert'; import { assertDev, assertSync, strictAssert } from '../util/assert';
import { combineNames } from '../util/combineNames'; import { combineNames } from '../util/combineNames';
@ -193,7 +195,7 @@ const dataInterface: ServerInterface = {
getKyberPreKeyById, getKyberPreKeyById,
bulkAddKyberPreKeys, bulkAddKyberPreKeys,
removeKyberPreKeyById, removeKyberPreKeyById,
removeKyberPreKeysByUuid, removeKyberPreKeysByServiceId,
removeAllKyberPreKeys, removeAllKyberPreKeys,
getAllKyberPreKeys, getAllKyberPreKeys,
@ -201,7 +203,7 @@ const dataInterface: ServerInterface = {
getPreKeyById, getPreKeyById,
bulkAddPreKeys, bulkAddPreKeys,
removePreKeyById, removePreKeyById,
removePreKeysByUuid, removePreKeysByServiceId,
removeAllPreKeys, removeAllPreKeys,
getAllPreKeys, getAllPreKeys,
@ -209,7 +211,7 @@ const dataInterface: ServerInterface = {
getSignedPreKeyById, getSignedPreKeyById,
bulkAddSignedPreKeys, bulkAddSignedPreKeys,
removeSignedPreKeyById, removeSignedPreKeyById,
removeSignedPreKeysByUuid, removeSignedPreKeysByServiceId,
removeAllSignedPreKeys, removeAllSignedPreKeys,
getAllSignedPreKeys, getAllSignedPreKeys,
@ -242,7 +244,7 @@ const dataInterface: ServerInterface = {
bulkAddSessions, bulkAddSessions,
removeSessionById, removeSessionById,
removeSessionsByConversation, removeSessionsByConversation,
removeSessionsByUUID, removeSessionsByServiceId,
removeAllSessions, removeAllSessions,
getAllSessions, getAllSessions,
@ -260,7 +262,7 @@ const dataInterface: ServerInterface = {
getAllConversations, getAllConversations,
getAllConversationIds, getAllConversationIds,
getAllGroupsInvolvingUuid, getAllGroupsInvolvingServiceId,
searchMessages, searchMessages,
@ -707,10 +709,12 @@ async function removeKyberPreKeyById(
): Promise<void> { ): Promise<void> {
return removeById(getInstance(), KYBER_PRE_KEYS_TABLE, id); return removeById(getInstance(), KYBER_PRE_KEYS_TABLE, id);
} }
async function removeKyberPreKeysByUuid(uuid: UUIDStringType): Promise<void> { async function removeKyberPreKeysByServiceId(
serviceId: ServiceIdString
): Promise<void> {
const db = getInstance(); const db = getInstance();
db.prepare<Query>('DELETE FROM kyberPreKeys WHERE ourUuid IS $uuid;').run({ db.prepare<Query>('DELETE FROM kyberPreKeys WHERE ourUuid IS $uuid;').run({
uuid, uuid: serviceId,
}); });
} }
async function removeAllKyberPreKeys(): Promise<void> { async function removeAllKyberPreKeys(): Promise<void> {
@ -737,10 +741,12 @@ async function removePreKeyById(
): Promise<void> { ): Promise<void> {
return removeById(getInstance(), PRE_KEYS_TABLE, id); return removeById(getInstance(), PRE_KEYS_TABLE, id);
} }
async function removePreKeysByUuid(uuid: UUIDStringType): Promise<void> { async function removePreKeysByServiceId(
serviceId: ServiceIdString
): Promise<void> {
const db = getInstance(); const db = getInstance();
db.prepare<Query>('DELETE FROM preKeys WHERE ourUuid IS $uuid;').run({ db.prepare<Query>('DELETE FROM preKeys WHERE ourUuid IS $uuid;').run({
uuid, uuid: serviceId,
}); });
} }
async function removeAllPreKeys(): Promise<void> { async function removeAllPreKeys(): Promise<void> {
@ -771,10 +777,12 @@ async function removeSignedPreKeyById(
): Promise<void> { ): Promise<void> {
return removeById(getInstance(), SIGNED_PRE_KEYS_TABLE, id); return removeById(getInstance(), SIGNED_PRE_KEYS_TABLE, id);
} }
async function removeSignedPreKeysByUuid(uuid: UUIDStringType): Promise<void> { async function removeSignedPreKeysByServiceId(
serviceId: ServiceIdString
): Promise<void> {
const db = getInstance(); const db = getInstance();
db.prepare<Query>('DELETE FROM signedPreKeys WHERE ourUuid IS $uuid;').run({ db.prepare<Query>('DELETE FROM signedPreKeys WHERE ourUuid IS $uuid;').run({
uuid, uuid: serviceId,
}); });
} }
async function removeAllSignedPreKeys(): Promise<void> { async function removeAllSignedPreKeys(): Promise<void> {
@ -941,14 +949,18 @@ async function insertSentProto(
` `
); );
const recipientUuids = Object.keys(recipients); const recipientServiceIds = Object.keys(recipients);
for (const recipientUuid of recipientUuids) { for (const recipientServiceId of recipientServiceIds) {
const deviceIds = recipients[recipientUuid]; strictAssert(
isServiceIdString(recipientServiceId),
'Recipient must be a service id'
);
const deviceIds = recipients[recipientServiceId];
for (const deviceId of deviceIds) { for (const deviceId of deviceIds) {
recipientStatement.run({ recipientStatement.run({
id, id,
recipientUuid, recipientUuid: recipientServiceId,
deviceId, deviceId,
}); });
} }
@ -1013,11 +1025,11 @@ async function deleteSentProtoByMessageId(messageId: string): Promise<void> {
async function insertProtoRecipients({ async function insertProtoRecipients({
id, id,
recipientUuid, recipientServiceId,
deviceIds, deviceIds,
}: { }: {
id: number; id: number;
recipientUuid: string; recipientServiceId: ServiceIdString;
deviceIds: Array<number>; deviceIds: Array<number>;
}): Promise<void> { }): Promise<void> {
const db = getInstance(); const db = getInstance();
@ -1041,7 +1053,7 @@ async function insertProtoRecipients({
for (const deviceId of deviceIds) { for (const deviceId of deviceIds) {
statement.run({ statement.run({
id, id,
recipientUuid, recipientUuid: recipientServiceId,
deviceId, deviceId,
}); });
} }
@ -1064,7 +1076,7 @@ async function deleteSentProtoRecipient(
const successfulPhoneNumberShares = new Array<string>(); const successfulPhoneNumberShares = new Array<string>();
for (const item of items) { for (const item of items) {
const { timestamp, recipientUuid, deviceId } = item; const { timestamp, recipientServiceId, deviceId } = item;
// 1. Figure out what payload we're talking about. // 1. Figure out what payload we're talking about.
const rows = prepare( const rows = prepare(
@ -1079,7 +1091,7 @@ async function deleteSentProtoRecipient(
sendLogRecipients.recipientUuid = $recipientUuid AND sendLogRecipients.recipientUuid = $recipientUuid AND
sendLogRecipients.deviceId = $deviceId; sendLogRecipients.deviceId = $deviceId;
` `
).all({ timestamp, recipientUuid, deviceId }); ).all({ timestamp, recipientUuid: recipientServiceId, deviceId });
if (!rows.length) { if (!rows.length) {
continue; continue;
} }
@ -1102,7 +1114,7 @@ async function deleteSentProtoRecipient(
recipientUuid = $recipientUuid AND recipientUuid = $recipientUuid AND
deviceId = $deviceId; deviceId = $deviceId;
` `
).run({ id, recipientUuid, deviceId }); ).run({ id, recipientUuid: recipientServiceId, deviceId });
// 3. See how many more recipient devices there were for this payload. // 3. See how many more recipient devices there were for this payload.
const remainingDevices = prepare( const remainingDevices = prepare(
@ -1112,17 +1124,17 @@ async function deleteSentProtoRecipient(
WHERE payloadId = $id AND recipientUuid = $recipientUuid; WHERE payloadId = $id AND recipientUuid = $recipientUuid;
`, `,
{ pluck: true } { pluck: true }
).get({ id, recipientUuid }); ).get({ id, recipientUuid: recipientServiceId });
// 4. If there are no remaining devices for this recipient and we included // 4. If there are no remaining devices for this recipient and we included
// the pni signature in the proto - return the recipient to the caller. // the pni signature in the proto - return the recipient to the caller.
if (remainingDevices === 0 && hasPniSignatureMessage) { if (remainingDevices === 0 && hasPniSignatureMessage) {
logger.info( logger.info(
'deleteSentProtoRecipient: ' + 'deleteSentProtoRecipient: ' +
`Successfully shared phone number with ${recipientUuid} ` + `Successfully shared phone number with ${recipientServiceId} ` +
`through message ${timestamp}` `through message ${timestamp}`
); );
successfulPhoneNumberShares.push(recipientUuid); successfulPhoneNumberShares.push(recipientServiceId);
} }
strictAssert( strictAssert(
@ -1163,11 +1175,11 @@ async function deleteSentProtoRecipient(
async function getSentProtoByRecipient({ async function getSentProtoByRecipient({
now, now,
recipientUuid, recipientServiceId,
timestamp, timestamp,
}: { }: {
now: number; now: number;
recipientUuid: string; recipientServiceId: ServiceIdString;
timestamp: number; timestamp: number;
}): Promise<SentProtoWithMessageIdsType | undefined> { }): Promise<SentProtoWithMessageIdsType | undefined> {
const db = getInstance(); const db = getInstance();
@ -1193,7 +1205,7 @@ async function getSentProtoByRecipient({
` `
).get({ ).get({
timestamp, timestamp,
recipientUuid, recipientUuid: recipientServiceId,
}); });
if (!row) { if (!row) {
@ -1348,7 +1360,9 @@ async function removeSessionsByConversation(
conversationId, conversationId,
}); });
} }
async function removeSessionsByUUID(uuid: UUIDStringType): Promise<void> { async function removeSessionsByServiceId(
serviceId: ServiceIdString
): Promise<void> {
const db = getInstance(); const db = getInstance();
db.prepare<Query>( db.prepare<Query>(
` `
@ -1356,7 +1370,7 @@ async function removeSessionsByUUID(uuid: UUIDStringType): Promise<void> {
WHERE uuid = $uuid; WHERE uuid = $uuid;
` `
).run({ ).run({
uuid, uuid: serviceId,
}); });
} }
async function removeAllSessions(): Promise<void> { async function removeAllSessions(): Promise<void> {
@ -1641,8 +1655,8 @@ async function getAllConversationIds(): Promise<Array<string>> {
return rows.map(row => row.id); return rows.map(row => row.id);
} }
async function getAllGroupsInvolvingUuid( async function getAllGroupsInvolvingServiceId(
uuid: UUIDStringType serviceId: ServiceIdString
): Promise<Array<ConversationType>> { ): Promise<Array<ConversationType>> {
const db = getInstance(); const db = getInstance();
const rows: ConversationRows = db const rows: ConversationRows = db
@ -1656,7 +1670,7 @@ async function getAllGroupsInvolvingUuid(
` `
) )
.all({ .all({
uuid: `%${uuid}%`, uuid: `%${serviceId}%`,
}); });
return rows.map(row => rowToConversation(row)); return rows.map(row => rowToConversation(row));
@ -1889,7 +1903,7 @@ function saveMessageSync(
db?: Database; db?: Database;
forceSave?: boolean; forceSave?: boolean;
jobToInsert?: StoredJob; jobToInsert?: StoredJob;
ourUuid: UUIDStringType; ourAci: AciString;
} }
): string { ): string {
const { const {
@ -1897,7 +1911,7 @@ function saveMessageSync(
db = getInstance(), db = getInstance(),
forceSave, forceSave,
jobToInsert, jobToInsert,
ourUuid, ourAci,
} = options; } = options;
if (!alreadyInTransaction) { if (!alreadyInTransaction) {
@ -1976,7 +1990,7 @@ function saveMessageSync(
hasAttachments: hasAttachments ? 1 : 0, hasAttachments: hasAttachments ? 1 : 0,
hasFileAttachments: hasFileAttachments ? 1 : 0, hasFileAttachments: hasFileAttachments ? 1 : 0,
hasVisualMediaAttachments: hasVisualMediaAttachments ? 1 : 0, hasVisualMediaAttachments: hasVisualMediaAttachments ? 1 : 0,
isChangeCreatedByUs: groupV2Change?.from === ourUuid ? 1 : 0, isChangeCreatedByUs: groupV2Change?.from === ourAci ? 1 : 0,
isErased: isErased ? 1 : 0, isErased: isErased ? 1 : 0,
isViewOnce: isViewOnce ? 1 : 0, isViewOnce: isViewOnce ? 1 : 0,
mentionsMe: mentionsMe ? 1 : 0, mentionsMe: mentionsMe ? 1 : 0,
@ -2038,7 +2052,7 @@ function saveMessageSync(
const toCreate = { const toCreate = {
...data, ...data,
id: id || UUID.generate().toString(), id: id || generateUuid(),
}; };
prepare( prepare(
@ -2119,7 +2133,7 @@ async function saveMessage(
jobToInsert?: StoredJob; jobToInsert?: StoredJob;
forceSave?: boolean; forceSave?: boolean;
alreadyInTransaction?: boolean; alreadyInTransaction?: boolean;
ourUuid: UUIDStringType; ourAci: AciString;
} }
): Promise<string> { ): Promise<string> {
return saveMessageSync(data, options); return saveMessageSync(data, options);
@ -2127,7 +2141,7 @@ async function saveMessage(
async function saveMessages( async function saveMessages(
arrayOfMessages: ReadonlyArray<MessageType>, arrayOfMessages: ReadonlyArray<MessageType>,
options: { forceSave?: boolean; ourUuid: UUIDStringType } options: { forceSave?: boolean; ourAci: AciString }
): Promise<void> { ): Promise<void> {
const db = getInstance(); const db = getInstance();
@ -2235,7 +2249,7 @@ async function getMessageBySender({
sent_at, sent_at,
}: { }: {
source?: string; source?: string;
sourceUuid?: UUIDStringType; sourceUuid?: ServiceIdString;
sourceDevice?: number; sourceDevice?: number;
sent_at: number; sent_at: number;
}): Promise<MessageType | undefined> { }): Promise<MessageType | undefined> {
@ -2432,7 +2446,7 @@ async function getUnreadReactionsAndMarkRead({
} }
async function markReactionAsRead( async function markReactionAsRead(
targetAuthorUuid: string, targetAuthorServiceId: ServiceIdString,
targetTimestamp: number targetTimestamp: number
): Promise<ReactionType | undefined> { ): Promise<ReactionType | undefined> {
const db = getInstance(); const db = getInstance();
@ -2451,7 +2465,7 @@ async function markReactionAsRead(
` `
) )
.get({ .get({
targetAuthorUuid, targetAuthorUuid: targetAuthorServiceId,
targetTimestamp, targetTimestamp,
}); });
@ -2463,7 +2477,7 @@ async function markReactionAsRead(
targetTimestamp = $targetTimestamp; targetTimestamp = $targetTimestamp;
` `
).run({ ).run({
targetAuthorUuid, targetAuthorUuid: targetAuthorServiceId,
targetTimestamp, targetTimestamp,
}); });
@ -2518,12 +2532,12 @@ async function addReaction({
async function removeReactionFromConversation({ async function removeReactionFromConversation({
emoji, emoji,
fromId, fromId,
targetAuthorUuid, targetAuthorServiceId,
targetTimestamp, targetTimestamp,
}: { }: {
emoji: string; emoji: string;
fromId: string; fromId: string;
targetAuthorUuid: string; targetAuthorServiceId: ServiceIdString;
targetTimestamp: number; targetTimestamp: number;
}): Promise<void> { }): Promise<void> {
const db = getInstance(); const db = getInstance();
@ -2538,7 +2552,7 @@ async function removeReactionFromConversation({
.run({ .run({
emoji, emoji,
fromId, fromId,
targetAuthorUuid, targetAuthorUuid: targetAuthorServiceId,
targetTimestamp, targetTimestamp,
}); });
} }
@ -2711,7 +2725,7 @@ async function getAllStories({
sourceUuid, sourceUuid,
}: { }: {
conversationId?: string; conversationId?: string;
sourceUuid?: UUIDStringType; sourceUuid?: ServiceIdString;
}): Promise<GetAllStoriesResultType> { }): Promise<GetAllStoriesResultType> {
const db = getInstance(); const db = getInstance();
const rows: ReadonlyArray<{ const rows: ReadonlyArray<{
@ -2912,11 +2926,9 @@ async function getNearbyMessageFromDeletedSet({
function getLastConversationActivity({ function getLastConversationActivity({
conversationId, conversationId,
includeStoryReplies, includeStoryReplies,
ourUuid,
}: { }: {
conversationId: string; conversationId: string;
includeStoryReplies: boolean; includeStoryReplies: boolean;
ourUuid: UUIDStringType;
}): MessageType | undefined { }): MessageType | undefined {
const db = getInstance(); const db = getInstance();
const row = prepare( const row = prepare(
@ -2935,7 +2947,6 @@ function getLastConversationActivity({
` `
).get({ ).get({
conversationId, conversationId,
ourUuid,
}); });
if (!row) { if (!row) {
@ -2988,11 +2999,9 @@ function getLastConversationPreview({
async function getConversationMessageStats({ async function getConversationMessageStats({
conversationId, conversationId,
includeStoryReplies, includeStoryReplies,
ourUuid,
}: { }: {
conversationId: string; conversationId: string;
includeStoryReplies: boolean; includeStoryReplies: boolean;
ourUuid: UUIDStringType;
}): Promise<ConversationMessageStatsType> { }): Promise<ConversationMessageStatsType> {
const db = getInstance(); const db = getInstance();
@ -3001,7 +3010,6 @@ async function getConversationMessageStats({
activity: getLastConversationActivity({ activity: getLastConversationActivity({
conversationId, conversationId,
includeStoryReplies, includeStoryReplies,
ourUuid,
}), }),
preview: getLastConversationPreview({ preview: getLastConversationPreview({
conversationId, conversationId,
@ -3318,7 +3326,7 @@ async function getCallHistoryMessageByCallId(options: {
async function getCallHistory( async function getCallHistory(
callId: string, callId: string,
peerId: string peerId: ServiceIdString | string
): Promise<CallHistoryDetails | undefined> { ): Promise<CallHistoryDetails | undefined> {
const db = getInstance(); const db = getInstance();
@ -5292,7 +5300,7 @@ function modifyStoryDistributionMembersSync(
{ {
toAdd, toAdd,
toRemove, toRemove,
}: { toAdd: Array<UUIDStringType>; toRemove: Array<UUIDStringType> } }: { toAdd: Array<ServiceIdString>; toRemove: Array<ServiceIdString> }
) { ) {
const memberInsertStatement = prepare( const memberInsertStatement = prepare(
db, db,
@ -5314,7 +5322,7 @@ function modifyStoryDistributionMembersSync(
}); });
} }
batchMultiVarQuery(db, toRemove, (uuids: ReadonlyArray<UUIDStringType>) => { batchMultiVarQuery(db, toRemove, (uuids: ReadonlyArray<ServiceIdString>) => {
db.prepare<ArrayQuery>( db.prepare<ArrayQuery>(
` `
DELETE FROM storyDistributionMembers DELETE FROM storyDistributionMembers
@ -5328,7 +5336,7 @@ async function modifyStoryDistributionWithMembers(
{ {
toAdd, toAdd,
toRemove, toRemove,
}: { toAdd: Array<UUIDStringType>; toRemove: Array<UUIDStringType> } }: { toAdd: Array<ServiceIdString>; toRemove: Array<ServiceIdString> }
): Promise<void> { ): Promise<void> {
const payload = freezeStoryDistribution(distribution); const payload = freezeStoryDistribution(distribution);
const db = getInstance(); const db = getInstance();
@ -5354,7 +5362,7 @@ async function modifyStoryDistributionMembers(
{ {
toAdd, toAdd,
toRemove, toRemove,
}: { toAdd: Array<UUIDStringType>; toRemove: Array<UUIDStringType> } }: { toAdd: Array<ServiceIdString>; toRemove: Array<ServiceIdString> }
): Promise<void> { ): Promise<void> {
const db = getInstance(); const db = getInstance();
@ -5362,7 +5370,9 @@ async function modifyStoryDistributionMembers(
modifyStoryDistributionMembersSync(db, listId, { toAdd, toRemove }); modifyStoryDistributionMembersSync(db, listId, { toAdd, toRemove });
})(); })();
} }
async function deleteStoryDistribution(id: UUIDStringType): Promise<void> { async function deleteStoryDistribution(
id: StoryDistributionIdString
): Promise<void> {
const db = getInstance(); const db = getInstance();
db.prepare<Query>('DELETE FROM storyDistributions WHERE id = $id;').run({ db.prepare<Query>('DELETE FROM storyDistributions WHERE id = $id;').run({
id, id,
@ -5402,8 +5412,8 @@ async function getLastStoryReadsForAuthor({
conversationId, conversationId,
limit: initialLimit, limit: initialLimit,
}: { }: {
authorId: UUIDStringType; authorId: ServiceIdString;
conversationId?: UUIDStringType; conversationId?: string;
limit?: number; limit?: number;
}): Promise<Array<StoryReadType>> { }): Promise<Array<StoryReadType>> {
const limit = initialLimit || 5; const limit = initialLimit || 5;
@ -6228,7 +6238,7 @@ async function removeAllProfileKeyCredentials(): Promise<void> {
async function saveEditedMessage( async function saveEditedMessage(
mainMessage: MessageType, mainMessage: MessageType,
ourUuid: UUIDStringType, ourAci: AciString,
{ conversationId, messageId, readStatus, sentAt }: EditedMessageType { conversationId, messageId, readStatus, sentAt }: EditedMessageType
): Promise<void> { ): Promise<void> {
const db = getInstance(); const db = getInstance();
@ -6236,7 +6246,7 @@ async function saveEditedMessage(
db.transaction(() => { db.transaction(() => {
assertSync( assertSync(
saveMessageSync(mainMessage, { saveMessageSync(mainMessage, {
ourUuid, ourAci,
alreadyInTransaction: true, alreadyInTransaction: true,
}) })
); );

View file

@ -4,7 +4,7 @@
import type { Database } from '@signalapp/better-sqlite3'; import type { Database } from '@signalapp/better-sqlite3';
import type { LoggerType } from '../../types/Logging'; import type { LoggerType } from '../../types/Logging';
import { isValidUuid } from '../../types/UUID'; import { isValidUuid } from '../../util/isValidUuid';
import { assertSync } from '../../util/assert'; import { assertSync } from '../../util/assert';
import Helpers from '../../textsecure/Helpers'; import Helpers from '../../textsecure/Helpers';
import { createOrUpdate, getById, removeById } from '../util'; import { createOrUpdate, getById, removeById } from '../util';

View file

@ -5,7 +5,8 @@ import type { Database } from '@signalapp/better-sqlite3';
import { omit } from 'lodash'; import { omit } from 'lodash';
import type { LoggerType } from '../../types/Logging'; import type { LoggerType } from '../../types/Logging';
import type { UUIDStringType } from '../../types/UUID'; import type { AciString, ServiceIdString } from '../../types/ServiceId';
import { normalizeAci } from '../../types/ServiceId';
import { isNotNil } from '../../util/isNotNil'; import { isNotNil } from '../../util/isNotNil';
import { assertDev } from '../../util/assert'; import { assertDev } from '../../util/assert';
import { import {
@ -96,7 +97,7 @@ export default function updateToSchemaVersion43(
const newValue = oldValue const newValue = oldValue
.map(member => { .map(member => {
const uuid: UUIDStringType = getConversationUuid.get({ const uuid: ServiceIdString = getConversationUuid.get({
conversationId: member.conversationId, conversationId: member.conversationId,
}); });
if (!uuid) { if (!uuid) {
@ -117,7 +118,7 @@ export default function updateToSchemaVersion43(
return updated; return updated;
} }
const addedByUserId: UUIDStringType | undefined = const addedByUserId: ServiceIdString | undefined =
getConversationUuid.get({ getConversationUuid.get({
conversationId: member.addedByUserId, conversationId: member.addedByUserId,
}); });
@ -227,7 +228,7 @@ export default function updateToSchemaVersion43(
if (groupV2Change) { if (groupV2Change) {
assertDev(result.groupV2Change, 'Pacify typescript'); assertDev(result.groupV2Change, 'Pacify typescript');
const from: UUIDStringType | undefined = getConversationUuid.get({ const from: AciString | undefined = getConversationUuid.get({
conversationId: groupV2Change.from, conversationId: groupV2Change.from,
}); });
@ -262,7 +263,7 @@ export default function updateToSchemaVersion43(
} }
changedDetails = true; changedDetails = true;
const newValue: UUIDStringType | null = getConversationUuid.get({ const newValue: ServiceIdString | null = getConversationUuid.get({
conversationId: oldValue, conversationId: oldValue,
}); });
if (key === 'inviter' && !newValue) { if (key === 'inviter' && !newValue) {
@ -302,7 +303,7 @@ export default function updateToSchemaVersion43(
} }
if (sourceUuid) { if (sourceUuid) {
const newValue: UUIDStringType | null = getConversationUuid.get({ const newValue: ServiceIdString | null = getConversationUuid.get({
conversationId: sourceUuid, conversationId: sourceUuid,
}); });
@ -317,7 +318,7 @@ export default function updateToSchemaVersion43(
if (invitedGV2Members) { if (invitedGV2Members) {
const newMembers = invitedGV2Members const newMembers = invitedGV2Members
.map(({ addedByUserId, conversationId }, i) => { .map(({ addedByUserId, conversationId }, i) => {
const uuid: UUIDStringType | null = getConversationUuid.get({ const uuid: ServiceIdString | null = getConversationUuid.get({
conversationId, conversationId,
}); });
const oldMember = const oldMember =
@ -341,7 +342,7 @@ export default function updateToSchemaVersion43(
return newMember; return newMember;
} }
const newAddedBy: UUIDStringType | null = getConversationUuid.get({ const newAddedBy: ServiceIdString | null = getConversationUuid.get({
conversationId: addedByUserId, conversationId: addedByUserId,
}); });
if (!newAddedBy) { if (!newAddedBy) {
@ -350,7 +351,7 @@ export default function updateToSchemaVersion43(
return { return {
...newMember, ...newMember,
addedByUserId: newAddedBy, addedByUserId: normalizeAci(newAddedBy, 'migration-43'),
}; };
}) })
.filter(isNotNil); .filter(isNotNil);

View file

@ -4,7 +4,7 @@
import type { Database } from '@signalapp/better-sqlite3'; import type { Database } from '@signalapp/better-sqlite3';
import type { LoggerType } from '../../types/Logging'; import type { LoggerType } from '../../types/Logging';
import type { UUIDStringType } from '../../types/UUID'; import type { ServiceIdString } from '../../types/ServiceId';
import { jsonToObject } from '../util'; import { jsonToObject } from '../util';
import type { EmptyQuery } from '../util'; import type { EmptyQuery } from '../util';
import type { ConversationType } from '../Interface'; import type { ConversationType } from '../Interface';
@ -21,7 +21,7 @@ export default function updateToSchemaVersion53(
type LegacyConversationType = { type LegacyConversationType = {
id: string; id: string;
groupId: string; groupId: string;
bannedMembersV2?: Array<UUIDStringType>; bannedMembersV2?: Array<ServiceIdString>;
}; };
const updateConversationStmt = db.prepare( const updateConversationStmt = db.prepare(

Some files were not shown because too many files have changed in this diff Show more