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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,6 +14,7 @@ import {
GroupCallConnectionState,
GroupCallJoinState,
} from '../types/Calling';
import { generateAci } from '../types/ServiceId';
import type { ConversationType } from '../state/ducks/conversations';
import { AvatarColors } from '../types/Colors';
import type { PropsType } from './CallScreen';
@ -174,7 +175,7 @@ const createProps = (
name: 'Morty Smith',
profileName: 'Morty Smith',
title: 'Morty Smith',
uuid: '3c134598-eecb-42ab-9ad3-2b0873f771b2',
uuid: generateAci(),
}),
openSystemPreferencesAction: action('open-system-preferences-action'),
setGroupCallVideoRequest: action('set-group-call-video-request'),
@ -311,7 +312,7 @@ export function GroupCall1(): JSX.Element {
videoAspectRatio: 1.3,
...getDefaultConversation({
isBlocked: false,
uuid: '72fa60e5-25fb-472d-8a56-e56867c57dda',
uuid: generateAci(),
title: 'Tyler',
}),
},
@ -379,7 +380,7 @@ export function GroupCallReconnecting(): JSX.Element {
...getDefaultConversation({
isBlocked: false,
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 { boolean } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import { v4 as generateUuid } from 'uuid';
import { AvatarColors } from '../types/Colors';
import type { ConversationType } from '../state/ducks/conversations';
import type { PropsType } from './CallingLobby';
import { CallingLobby } from './CallingLobby';
import { setupI18n } from '../util/setupI18n';
import { UUID } from '../types/UUID';
import { generateAci } from '../types/ServiceId';
import enMessages from '../../_locales/en/messages.json';
import {
getDefaultConversation,
@ -65,8 +66,8 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => {
overrideProps.me ||
getDefaultConversation({
color: AvatarColors[0],
id: UUID.generate().toString(),
uuid: UUID.generate().toString(),
id: generateUuid(),
uuid: generateAci(),
}),
onCallCanceled: action('on-call-canceled'),
onJoinCall: action('on-join-call'),
@ -116,8 +117,8 @@ export function NoCameraLocalAvatar(): JSX.Element {
me: getDefaultConversation({
avatarPath: '/fixtures/kitten-4-112-112.jpg',
color: AvatarColors[0],
id: UUID.generate().toString(),
uuid: UUID.generate().toString(),
id: generateUuid(),
uuid: generateAci(),
}),
});
return <CallingLobby {...props} />;
@ -167,11 +168,11 @@ GroupCall1PeekedParticipant.story = {
};
export function GroupCall1PeekedParticipantSelf(): JSX.Element {
const uuid = UUID.generate().toString();
const uuid = generateAci();
const props = createProps({
isGroupCall: true,
me: getDefaultConversation({
id: UUID.generate().toString(),
id: generateUuid(),
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 { CompositionInput } from './CompositionInput';
import { setupI18n } from '../util/setupI18n';
import { generateAci } from '../types/ServiceId';
import enMessages from '../../_locales/en/messages.json';
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
@ -137,7 +138,7 @@ export function Mentions(): JSX.Element {
{
start: 5,
length: 1,
mentionUuid: '0',
mentionUuid: generateAci(),
conversationID: 'k',
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 { ConversationType } from '../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import { isValidUuid } from '../types/UUID';
import { isServiceIdString } from '../types/ServiceId';
import { MentionBlot } from '../quill/mentions/blot';
import {
matchEmojiImage,
@ -46,6 +46,7 @@ import {
import { SignalClipboard } from '../quill/signal-clipboard';
import { DirectionalBlot } from '../quill/block/blot';
import { getClassNamesFor } from '../util/getClassNamesFor';
import { isNotNil } from '../util/isNotNil';
import * as log from '../logging/log';
import * as Errors from '../types/errors';
import { useRefMerger } from '../hooks/useRefMerger';
@ -677,11 +678,15 @@ export function CompositionInput(props: Props): React.ReactElement {
return;
}
const currentMemberUuids = currentMembers
const currentMemberServiceIds = currentMembers
.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
quill.updateContents(newDelta as any);

View file

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

View file

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

View file

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

View file

@ -10,6 +10,7 @@ import { GroupCallRemoteParticipant } from './GroupCallRemoteParticipant';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { FRAME_BUFFER_SIZE } from '../calling/constants';
import { setupI18n } from '../util/setupI18n';
import { generateAci } from '../types/ServiceId';
import enMessages from '../../_locales/en/messages.json';
const i18n = setupI18n('en', enMessages);
@ -60,7 +61,7 @@ const createProps = (
isBlocked: Boolean(isBlocked),
title:
'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,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,7 +21,8 @@ import {
Page as StoriesSettingsPage,
} from './StoriesSettingsModal';
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 { Avatar, AvatarSize } from './Avatar';
import { Button, ButtonSize, ButtonVariant } from './Button';
@ -54,18 +55,18 @@ export type PropsType = {
i18n: LocalizerType;
me: ConversationType;
onClose: () => unknown;
onDeleteList: (listId: string) => unknown;
onDeleteList: (listId: StoryDistributionIdString) => unknown;
onDistributionListCreated: (
name: string,
viewerUuids: Array<UUIDStringType>
) => Promise<UUIDStringType>;
viewerUuids: Array<ServiceIdString>
) => Promise<StoryDistributionIdString>;
onSelectedStoryList: (options: {
conversationId: string;
distributionId: string | undefined;
uuids: Array<UUIDStringType>;
distributionId: StoryDistributionIdString | undefined;
serviceIds: Array<ServiceIdString>;
}) => unknown;
onSend: (
listIds: Array<UUIDStringType>,
listIds: Array<StoryDistributionIdString>,
conversationIds: Array<string>
) => unknown;
signalConnections: Array<ConversationType>;
@ -99,21 +100,23 @@ const Page = {
type PageType = SendStoryPage | StoriesSettingsPage;
function getListMemberUuids(
function getListMemberServiceIds(
list: StoryDistributionListWithMembersDataType,
signalConnections: Array<ConversationType>
): Array<UUIDStringType> {
const memberUuids = list.members.map(({ uuid }) => uuid).filter(isNotNil);
): Array<ServiceIdString> {
const memberServiceIds = list.members
.map(({ uuid }) => uuid)
.filter(isNotNil);
if (list.id === MY_STORY_ID && list.isBlockList) {
const excludeUuids = new Set<string>(memberUuids);
const excludeUuids = new Set<string>(memberServiceIds);
return signalConnections
.map(conversation => conversation.uuid)
.filter(isNotNil)
.filter(uuid => !excludeUuids.has(uuid));
}
return memberUuids;
return memberServiceIds;
}
export function SendStoryModal({
@ -147,9 +150,9 @@ export function SendStoryModal({
const [confirmDiscardModal, confirmDiscardIf] = useConfirmDiscard(i18n);
const [selectedListIds, setSelectedListIds] = useState<Set<UUIDStringType>>(
new Set()
);
const [selectedListIds, setSelectedListIds] = useState<
Set<StoryDistributionIdString>
>(new Set());
const [selectedGroupIds, setSelectedGroupIds] = useState<Set<string>>(
new Set()
);
@ -215,7 +218,7 @@ export function SendStoryModal({
string | undefined
>();
const [confirmDeleteList, setConfirmDeleteList] = useState<
{ id: string; name: string } | undefined
{ id: StoryDistributionIdString; name: string } | undefined
>();
const [listIdToEdit, setListIdToEdit] = useState<string | undefined>();
@ -263,7 +266,7 @@ export function SendStoryModal({
selectedNames = chosenGroupNames.join(', ');
} else {
selectedNames = selectedStoryNames
.map(listName => getStoryDistributionListName(i18n, listName, listName))
.map(listName => getStoryDistributionListName(i18n, undefined, listName))
.join(', ');
}
@ -661,7 +664,7 @@ export function SendStoryModal({
onSelectedStoryList({
conversationId: ourConversationId,
distributionId: list.id,
uuids: getListMemberUuids(list, signalConnections),
serviceIds: getListMemberServiceIds(list, signalConnections),
});
}
}}
@ -792,7 +795,7 @@ export function SendStoryModal({
onSelectedStoryList({
conversationId: group.id,
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,
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 { Row } from './ConversationList';
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 { Avatar, AvatarSize } from './Avatar';
import { Button, ButtonVariant } from './Button';
@ -28,7 +29,6 @@ import { SearchInput } from './SearchInput';
import { StoryDistributionListName } from './StoryDistributionListName';
import { Theme } from '../util/theme';
import { ThemeType } from '../types/Util';
import { UUID } from '../types/UUID';
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
import { isNotNil } from '../util/isNotNil';
import {
@ -54,23 +54,25 @@ export type PropsType = {
toggleGroupsForStorySend: (groupIds: Array<string>) => unknown;
onDistributionListCreated: (
name: string,
viewerUuids: Array<UUIDStringType>
viewerUuids: Array<ServiceIdString>
) => Promise<string>;
onHideMyStoriesFrom: (viewerUuids: Array<UUIDStringType>) => unknown;
onRemoveMembers: (listId: string, uuids: Array<UUIDStringType>) => unknown;
onHideMyStoriesFrom: (viewerUuids: Array<ServiceIdString>) => unknown;
onRemoveMembers: (listId: string, uuids: Array<ServiceIdString>) => unknown;
onRepliesNReactionsChanged: (
listId: string,
allowsReplies: boolean
) => unknown;
onViewersUpdated: (
listId: string,
viewerUuids: Array<UUIDStringType>
viewerUuids: Array<ServiceIdString>
) => unknown;
setMyStoriesToAllSignalConnections: () => unknown;
storyViewReceiptsEnabled: boolean;
toggleSignalConnectionsModal: () => unknown;
setStoriesDisabled: (value: boolean) => void;
getConversationByUuid: (uuid: UUIDStringType) => ConversationType | undefined;
getConversationByUuid: (
uuid: ServiceIdString
) => ConversationType | undefined;
};
export enum Page {
@ -134,7 +136,7 @@ type DistributionListItemProps = {
distributionList: StoryDistributionListWithMembersDataType;
me: ConversationType;
signalConnections: Array<ConversationType>;
onSelectItemToEdit(id: UUIDStringType): void;
onSelectItemToEdit(id: StoryDistributionIdString): void;
};
function DistributionListItem({
@ -543,7 +545,10 @@ type DistributionListSettingsModalPropsType = {
i18n: LocalizerType;
listToEdit: StoryDistributionListWithMembersDataType;
signalConnectionsCount: number;
setConfirmDeleteList: (_: { id: string; name: string }) => unknown;
setConfirmDeleteList: (_: {
id: StoryDistributionIdString;
name: string;
}) => unknown;
setPage: (page: Page) => unknown;
setSelectedContacts: (contacts: Array<ConversationType>) => unknown;
onBackButtonClick: (() => void) | undefined;
@ -577,7 +582,7 @@ export function DistributionListSettingsModal({
| {
listId: string;
title: string;
uuid: UUIDStringType;
uuid: ServiceIdString;
}
>();
@ -945,8 +950,8 @@ export function EditMyStoryPrivacy({
}
type EditDistributionListModalPropsType = {
onCreateList: (name: string, viewerUuids: Array<UUIDStringType>) => unknown;
onViewersUpdated: (viewerUuids: Array<UUIDStringType>) => unknown;
onCreateList: (name: string, viewerUuids: Array<ServiceIdString>) => unknown;
onViewersUpdated: (viewerUuids: Array<ServiceIdString>) => unknown;
page:
| Page.AddViewer
| Page.ChooseViewers
@ -998,7 +1003,7 @@ export function EditDistributionListModal({
return map;
}, [candidateConversations]);
const selectedConversationUuids: Set<UUIDStringType> = useMemo(
const selectConversationServiceIds: Set<ServiceIdString> = useMemo(
() =>
new Set(selectedContacts.map(contact => contact.uuid).filter(isNotNil)),
[selectedContacts]
@ -1034,7 +1039,7 @@ export function EditDistributionListModal({
<Button
disabled={!storyName}
onClick={() => {
onCreateList(storyName, Array.from(selectedConversationUuids));
onCreateList(storyName, Array.from(selectConversationServiceIds));
setStoryName('');
}}
variant={ButtonVariant.Primary}
@ -1105,7 +1110,7 @@ export function EditDistributionListModal({
return undefined;
}
const isSelected = selectedConversationUuids.has(UUID.cast(contact.uuid));
const isSelected = selectConversationServiceIds.has(contact.uuid);
return {
type: RowType.ContactCheckbox,
@ -1120,7 +1125,7 @@ export function EditDistributionListModal({
<Button
disabled={selectedContacts.length === 0}
onClick={() => {
onViewersUpdated(Array.from(selectedConversationUuids));
onViewersUpdated(Array.from(selectConversationServiceIds));
}}
variant={ButtonVariant.Primary}
>
@ -1132,7 +1137,7 @@ export function EditDistributionListModal({
<Button
disabled={selectedContacts.length === 0}
onClick={() => {
onViewersUpdated(Array.from(selectedConversationUuids));
onViewersUpdated(Array.from(selectConversationServiceIds));
}}
variant={ButtonVariant.Primary}
>
@ -1197,7 +1202,7 @@ export function EditDistributionListModal({
getPreferredBadge={getPreferredBadge}
getRow={getRow}
i18n={i18n}
lookupConversationWithoutUuid={asyncShouldNeverBeCalled}
lookupConversationWithoutServiceId={asyncShouldNeverBeCalled}
onClickArchiveButton={shouldNeverBeCalled}
onClickContactCheckbox={(conversationId: string) => {
toggleSelectedConversation(conversationId);
@ -1236,7 +1241,7 @@ type GroupStorySettingsModalProps = {
group: ConversationType;
onClose(): void;
onBackButtonClick(): void;
getConversationByUuid(uuid: UUIDStringType): ConversationType | undefined;
getConversationByUuid(uuid: ServiceIdString): ConversationType | undefined;
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 { Props as StickerButtonProps } from './stickers/StickerButton';
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 { PropsType as TextStoryCreatorPropsType } from './TextStoryCreator';
@ -54,7 +54,7 @@ export type PropsType = {
linkPreview?: LinkPreviewType;
onClose: () => unknown;
onSend: (
listIds: Array<UUIDStringType>,
listIds: Array<StoryDistributionIdString>,
conversationIds: Array<string>,
attachment: AttachmentType,
bodyRanges: DraftBodyRanges | undefined

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -84,7 +84,7 @@ import type {
import { createRefMerger } from '../../util/refMerger';
import { emojiToData, getEmojiCount, hasNonEmojiText } from '../emoji/lib';
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 { BadgeImageTheme } from '../../badges/BadgeImageTheme';
import { getBadgeImageFileLocalPath } from '../../badges/getBadgeImageFileLocalPath';
@ -319,7 +319,7 @@ export type PropsActions = {
messageExpanded: (id: string, displayLimit: number) => unknown;
checkForAccount: (phoneNumber: string) => unknown;
startConversation: (e164: string, uuid: UUIDStringType) => void;
startConversation: (e164: string, uuid: ServiceIdString) => void;
showConversation: ShowConversationType;
openGiftBadge: (messageId: string) => void;
pushPanelForConversation: PushPanelForConversationActionType;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -14,7 +14,7 @@ import { ChooseGroupMembersModal } from './AddGroupMembersModal/ChooseGroupMembe
import { ConfirmAdditionsModal } from './AddGroupMembersModal/ConfirmAdditionsModal';
import type { ConversationType } from '../../../state/ducks/conversations';
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 { DurationInSeconds } from '../../../util/durations';
import { NavTab } from '../../../state/ducks/nav';
@ -120,7 +120,7 @@ const createProps = (
getPreferredBadge={() => undefined}
theme={ThemeType.light}
i18n={i18n}
lookupConversationWithoutUuid={makeFakeLookupConversationWithoutUuid()}
lookupConversationWithoutServiceId={makeFakeLookupConversationWithoutServiceId()}
showUserNotFoundModal={action('showUserNotFoundModal')}
isUsernamesEnabled
/>

View file

@ -6,7 +6,7 @@ import { times } from 'lodash';
import { action } from '@storybook/addon-actions';
import { UUID } from '../../../types/UUID';
import { generateAci } from '../../../types/ServiceId';
import { StorySendMode } from '../../../types/Stories';
import { setupI18n } from '../../../util/setupI18n';
import enMessages from '../../../../_locales/en/messages.json';
@ -45,7 +45,7 @@ const conversation: ConversationType = {
storySendMode: StorySendMode.IfActive,
};
const OUR_UUID = UUID.generate().toString();
const OUR_UUID = generateAci();
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
approvePendingMembershipFromGroupV2: action(
@ -54,7 +54,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
conversation,
getPreferredBadge: () => undefined,
i18n,
ourUuid: OUR_UUID,
ourAci: OUR_UUID,
pendingApprovalMemberships: times(5, () => ({
member: getDefaultConversation(),
})),
@ -68,7 +68,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
...times(8, () => ({
member: getDefaultConversation(),
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 { LocalizerType, ThemeType } from '../../../types/Util';
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 { ConfirmationDialog } from '../../ConfirmationDialog';
import { PanelSection } from './PanelSection';
@ -21,7 +21,7 @@ export type PropsDataType = {
readonly conversation?: ConversationType;
readonly getPreferredBadge: PreferredBadgeSelectorType;
readonly i18n: LocalizerType;
readonly ourUuid: UUIDStringType;
readonly ourAci: AciString;
readonly pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
readonly pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
readonly theme: ThemeType;
@ -42,7 +42,7 @@ export type PropsType = PropsDataType & PropsActionType;
export type GroupV2PendingMembership = {
metadata: {
addedByUserId?: UUIDStringType;
addedByUserId?: AciString;
};
member: ConversationType;
};
@ -72,16 +72,14 @@ export function PendingInvites({
conversation,
getPreferredBadge,
i18n,
ourUuid,
ourAci,
pendingMemberships,
pendingApprovalMemberships,
revokePendingMembershipsFromGroupV2,
theme,
}: PropsType): JSX.Element {
if (!conversation || !ourUuid) {
throw new Error(
'PendingInvites rendered without a conversation or ourUuid'
);
if (!conversation || !ourAci) {
throw new Error('PendingInvites rendered without a conversation or ourAci');
}
const [stagedMemberships, setStagedMemberships] =
@ -126,7 +124,7 @@ export function PendingInvites({
i18n={i18n}
members={conversation.sortedGroupMembers || []}
memberships={pendingMemberships}
ourUuid={ourUuid}
ourAci={ourAci}
setStagedMemberships={setStagedMemberships}
theme={theme}
/>
@ -144,7 +142,7 @@ export function PendingInvites({
i18n={i18n}
members={conversation.sortedGroupMembers || []}
onClose={() => setStagedMemberships(null)}
ourUuid={ourUuid}
ourAci={ourAci}
revokePendingMembershipsFromGroupV2={
revokePendingMembershipsFromGroupV2
}
@ -161,7 +159,7 @@ function MembershipActionConfirmation({
i18n,
members,
onClose,
ourUuid,
ourAci,
revokePendingMembershipsFromGroupV2,
stagedMemberships,
}: {
@ -173,7 +171,7 @@ function MembershipActionConfirmation({
i18n: LocalizerType;
members: ReadonlyArray<ConversationType>;
onClose: () => void;
ourUuid: string;
ourAci: AciString;
revokePendingMembershipsFromGroupV2: (
conversationId: string,
memberIds: ReadonlyArray<string>
@ -234,7 +232,7 @@ function MembershipActionConfirmation({
conversation,
i18n,
members,
ourUuid,
ourAci,
stagedMemberships,
})}
</ConfirmationDialog>
@ -245,13 +243,13 @@ function getConfirmationMessage({
conversation,
i18n,
members,
ourUuid,
ourAci,
stagedMemberships,
}: Readonly<{
conversation: ConversationType;
i18n: LocalizerType;
members: ReadonlyArray<ConversationType>;
ourUuid: string;
ourAci: AciString;
stagedMemberships: ReadonlyArray<StagedMembershipType>;
}>): string {
if (!stagedMemberships || !stagedMemberships.length) {
@ -285,7 +283,7 @@ function getConfirmationMessage({
const firstPendingMembership = firstMembership as GroupV2PendingMembership;
// Pending invite
const invitedByUs = firstPendingMembership.metadata.addedByUserId === ourUuid;
const invitedByUs = firstPendingMembership.metadata.addedByUserId === ourAci;
if (invitedByUs) {
return i18n('icu:PendingInvites--revoke-for', {
@ -391,7 +389,7 @@ function MembersPendingProfileKey({
i18n,
members,
memberships,
ourUuid,
ourAci,
setStagedMemberships,
getPreferredBadge,
theme,
@ -401,7 +399,7 @@ function MembersPendingProfileKey({
i18n: LocalizerType;
members: ReadonlyArray<ConversationType>;
memberships: ReadonlyArray<GroupV2PendingMembership>;
ourUuid: string;
ourAci: AciString;
setStagedMemberships: (stagedMembership: Array<StagedMembershipType>) => void;
theme: ThemeType;
}>) {
@ -410,7 +408,7 @@ function MembersPendingProfileKey({
membership => membership.metadata.addedByUserId
);
const { [ourUuid]: ourPendingMemberships, ...otherPendingMembershipGroups } =
const { [ourAci]: ourPendingMemberships, ...otherPendingMembershipGroups } =
groupedPendingMemberships;
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 classNames from 'classnames';
import { isBoolean, isNumber } from 'lodash';
import { v4 as generateUuid } from 'uuid';
import { Avatar, AvatarSize } from '../Avatar';
import type { BadgeType } from '../../badges/types';
@ -16,7 +17,6 @@ import { Spinner } from '../Spinner';
import { Time } from '../Time';
import { formatDateTimeShort } from '../../util/timestamp';
import * as durations from '../../util/durations';
import { UUID } from '../../types/UUID';
const BASE_CLASS_NAME =
'module-conversation-list__item--contact-or-conversation';
@ -113,7 +113,7 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
} = props;
const identifier = id ? cleanId(id) : undefined;
const htmlId = useMemo(() => UUID.generate().toString(), []);
const htmlId = useMemo(() => generateUuid(), []);
const testId = overrideTestId || groupId || uuid;
const isUnread = isConversationUnread({ markedUnread, unreadCount });

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,14 +3,16 @@
import type { LocalizerType } from './types/Util';
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 type { GroupV2ChangeDetailType, GroupV2ChangeType } from './groups';
import { SignalService as Proto } from './protobuf';
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> = (
id: string,
i18n: LocalizerType,
@ -18,10 +20,11 @@ export type StringRendererType<T> = (
) => T | string;
export type RenderOptionsType<T> = {
from?: UUIDStringType;
// `from` will be a PNI when the change is "declining a PNI invite".
from?: ServiceIdString;
i18n: LocalizerType;
ourACI?: UUIDStringType;
ourPNI?: UUIDStringType;
ourAci: AciString | undefined;
ourPni: PniString | undefined;
renderContact: SmartContactRendererType<T>;
renderString: StringRendererType<T>;
};
@ -70,8 +73,8 @@ export function renderChangeDetail<T>(
const {
from,
i18n: localizer,
ourACI,
ourPNI,
ourAci,
ourPni,
renderContact,
renderString,
} = options;
@ -83,11 +86,11 @@ export function renderChangeDetail<T>(
return renderString(id, localizer, components);
}
const isOurUuid = (uuid?: UUIDStringType): boolean => {
const isOurUuid = (uuid?: ServiceIdString): boolean => {
if (!uuid) {
return false;
}
return Boolean((ourACI && uuid === ourACI) || (ourPNI && uuid === ourPNI));
return Boolean((ourAci && uuid === ourAci) || (ourPni && uuid === ourPni));
};
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 { SignalService as Proto } from '../protobuf';
import { ToastType } from '../types/Toast';
import { UUIDKind } from '../types/UUID';
import {
applyNewAvatar,
decryptGroupDescription,
@ -63,9 +62,9 @@ export async function joinViaLink(hash: string): Promise<void> {
const existingConversation =
window.ConversationController.get(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(
`joinViaLink/${logId}: Already a member of group, opening conversation`
);
@ -149,7 +148,7 @@ export async function joinViaLink(hash: string): Promise<void> {
if (
approvalRequired &&
existingConversation &&
existingConversation.isMemberAwaitingApproval(ourUuid)
existingConversation.isMemberAwaitingApproval(ourAci)
) {
log.warn(
`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.
if (
targetConversation &&
(targetConversation.hasMember(ourUuid) ||
(targetConversation.hasMember(ourAci) ||
(approvalRequired &&
targetConversation.isMemberAwaitingApproval(ourUuid)))
targetConversation.isMemberAwaitingApproval(ourAci)))
) {
log.warn(
`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 { ParsedJob } from './types';
import type SendMessage from '../textsecure/SendMessage';
import type { UUIDStringType } from '../types/UUID';
import type { ServiceIdString } from '../types/ServiceId';
import { commonShouldJobContinue } from './helpers/commonShouldJobContinue';
import { sleeper } from '../util/sleeper';
import { receiptSchema, ReceiptType } from '../types/Receipt';
@ -503,7 +503,7 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
}
}
} catch (error: unknown) {
const untrustedUuids: Array<UUIDStringType> = [];
const untrustedServiceIds: Array<ServiceIdString> = [];
const processError = (toProcess: unknown) => {
if (toProcess instanceof OutgoingIdentityKeyError) {
@ -512,14 +512,14 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
'private'
);
strictAssert(failedConversation, 'Conversation should be created');
const uuid = failedConversation.get('uuid');
if (!uuid) {
const serviceId = failedConversation.getServiceId();
if (!serviceId) {
log.error(
`failedConversation: Conversation ${failedConversation.idForLogging()} missing UUID!`
`failedConversation: Conversation ${failedConversation.idForLogging()} missing serviceId!`
);
return;
}
untrustedUuids.push(uuid);
untrustedServiceIds.push(serviceId);
} else if (toProcess instanceof SendMessageChallengeError) {
void window.Signal.challengeHandler?.register(
{
@ -541,29 +541,29 @@ export class ConversationJobQueue extends JobQueue<ConversationQueueJobData> {
(error.errors || []).forEach(processError);
}
if (untrustedUuids.length) {
if (untrustedServiceIds.length) {
if (type === jobSet.ProfileKey) {
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;
}
if (type === jobSet.Receipts) {
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;
}
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(
{
conversationId: conversation.id,
untrustedUuids,
untrustedServiceIds,
}
);
}

View file

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

View file

@ -23,7 +23,7 @@ import type {
ConversationQueueJobBundle,
DeleteForEveryoneJobData,
} from '../conversationJobQueue';
import { getUntrustedConversationUuids } from './getUntrustedConversationUuids';
import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
import { handleMessageSend } from '../../util/handleMessageSend';
import { isConversationAccepted } from '../../util/isConversationAccepted';
import { isConversationUnregistered } from '../../util/isConversationUnregistered';
@ -34,9 +34,9 @@ import type { MessageModel } from '../../models/messages';
import { SendMessageProtoError } from '../../textsecure/Errors';
import { strictAssert } from '../../util/assert';
import type { LoggerType } from '../../types/Logging';
import type { ServiceIdString } from '../../types/ServiceId';
import { isStory } from '../../messages/helpers';
import { sendToGroup } from '../../util/sendToGroup';
import { getTaggedConversationUuid } from '../../util/getConversationUuid';
export async function sendDeleteForEveryone(
conversation: ConversationModel,
@ -91,16 +91,20 @@ export async function sendDeleteForEveryone(
);
const recipients = deletedForEveryoneSendStatus
? getRecipients(deletedForEveryoneSendStatus)
: recipientsFromJob;
: recipientsFromJob
.map(recipient => {
return window.ConversationController.get(recipient)?.getServiceId();
})
.filter(isNotNil);
const untrustedUuids = getUntrustedConversationUuids(recipients);
if (untrustedUuids.length) {
const untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: conversation.id,
untrustedUuids,
untrustedServiceIds,
});
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
).finish(),
destination: conversation.get('e164'),
destinationUuid: getTaggedConversationUuid(
conversation.attributes
),
destinationServiceId: conversation.getServiceId(),
expirationStartTimestamp: null,
options: sendOptions,
timestamp,
@ -191,9 +193,9 @@ export async function sendDeleteForEveryone(
logId,
messageIds,
send: async sender =>
sender.sendMessageToIdentifier({
sender.sendMessageToServiceId({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
identifier: conversation.getSendTarget()!,
serviceId: conversation.getSendTarget()!,
messageText: undefined,
attachments: [],
deletedForEveryoneTimestamp: targetTimestamp,
@ -272,7 +274,7 @@ export async function sendDeleteForEveryone(
function getRecipients(
sendStatusByConversationId: Record<string, boolean>
): Array<string> {
): Array<ServiceIdString> {
return Object.entries(sendStatusByConversationId)
.filter(([_, isSent]) => !isSent)
.map(([conversationId]) => {
@ -286,7 +288,7 @@ function getRecipients(
if (recipient.isBlocked()) {
return null;
}
return recipient.get('uuid');
return recipient.getServiceId();
})
.filter(isNotNil);
}
@ -301,7 +303,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined,
});
await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
});
return;
@ -311,8 +313,8 @@ async function updateMessageWithSuccessfulSends(
...message.get('deletedForEveryoneSendStatus'),
};
result.successfulIdentifiers?.forEach(identifier => {
const conversation = window.ConversationController.get(identifier);
result.successfulServiceIds?.forEach(serviceId => {
const conversation = window.ConversationController.get(serviceId);
if (!conversation) {
return;
}
@ -324,7 +326,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined,
});
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 });
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,
DeleteStoryForEveryoneJobData,
} from '../conversationJobQueue';
import { getUntrustedConversationUuids } from './getUntrustedConversationUuids';
import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
import { handleMessageSend } from '../../util/handleMessageSend';
import { isConversationAccepted } from '../../util/isConversationAccepted';
import { isConversationUnregistered } from '../../util/isConversationUnregistered';
@ -82,14 +82,14 @@ export async function sendDeleteStoryForEveryone(
.filter(([_, isSent]) => !isSent)
.map(([conversationId]) => conversationId);
const untrustedUuids = getUntrustedConversationUuids(recipientIds);
if (untrustedUuids.length) {
const untrustedServiceIds = getUntrustedConversationServiceIds(recipientIds);
if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: ourConversation.id,
untrustedUuids,
untrustedServiceIds,
});
throw new Error(
`Delete for everyone blocked because ${untrustedUuids.length} ` +
`Delete for everyone blocked because ${untrustedServiceIds.length} ` +
'conversation(s) were untrusted. Failing this attempt.'
);
}
@ -171,10 +171,12 @@ export async function sendDeleteStoryForEveryone(
});
try {
const serviceId = conversation.getSendTarget();
strictAssert(serviceId, 'conversation has no service id');
await handleMessageSend(
messaging.sendMessageToIdentifier({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
identifier: conversation.getSendTarget()!,
messaging.sendMessageToServiceId({
serviceId,
messageText: undefined,
attachments: [],
deletedForEveryoneTimestamp: targetTimestamp,
@ -200,7 +202,7 @@ export async function sendDeleteStoryForEveryone(
await updateMessageWithSuccessfulSends(message, {
dataMessage: undefined,
editMessage: undefined,
successfulIdentifiers: [conversation.id],
successfulServiceIds: [serviceId],
});
} catch (error: unknown) {
if (error instanceof SendMessageProtoError) {
@ -231,17 +233,15 @@ export async function sendDeleteStoryForEveryone(
syncMessage: true,
});
const destinationUuid = ourConversation
.getCheckedUuid('deleteStoryForEveryone')
.toString();
const destinationServiceId = ourConversation.getCheckedServiceId(
'deleteStoryForEveryone'
);
// Sync message for other devices
await handleMessageSend(
messaging.sendSyncMessage({
destination: undefined,
destinationUuid: {
aci: destinationUuid,
},
destinationServiceId,
storyMessageRecipients: updatedStoryRecipients?.map(
({ destinationUuid: legacyDestinationUuid, ...rest }) => {
return {
@ -278,7 +278,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined,
});
await window.Signal.Data.saveMessage(message.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
});
return;
@ -288,8 +288,8 @@ async function updateMessageWithSuccessfulSends(
...message.get('deletedForEveryoneSendStatus'),
};
result.successfulIdentifiers?.forEach(identifier => {
const conversation = window.ConversationController.get(identifier);
result.successfulServiceIds?.forEach(serviceId => {
const conversation = window.ConversationController.get(serviceId);
if (!conversation) {
return;
}
@ -301,7 +301,7 @@ async function updateMessageWithSuccessfulSends(
deletedForEveryoneFailed: undefined,
});
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 });
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 { isConversationUnregistered } from '../../util/isConversationUnregistered';
import { DurationInSeconds } from '../../util/durations';
import { getTaggedConversationUuid } from '../../util/getConversationUuid';
export async function sendDirectExpirationTimerUpdate(
conversation: ConversationModel,
@ -47,14 +46,12 @@ export async function sendDirectExpirationTimerUpdate(
}
if (conversation.isUntrusted()) {
const uuid = conversation
.getCheckedUuid(
'Expiration timer send blocked: untrusted and missing uuid!'
)
.toString();
const serviceId = conversation.getCheckedServiceId(
'Expiration timer send blocked: untrusted and missing serviceId!'
);
window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: conversation.id,
untrustedUuids: [uuid],
untrustedServiceIds: [serviceId],
});
throw new Error(
'Expiration timer send blocked because conversation is untrusted. Failing this attempt.'
@ -108,7 +105,7 @@ export async function sendDirectExpirationTimerUpdate(
proto.dataMessage
).finish(),
destination: conversation.get('e164'),
destinationUuid: getTaggedConversationUuid(conversation.attributes),
destinationServiceId: conversation.getServiceId(),
expirationStartTimestamp: null,
options: sendOptions,
timestamp,
@ -143,7 +140,7 @@ export async function sendDirectExpirationTimerUpdate(
send: async sender =>
sender.sendIndividualProto({
contentHint,
identifier: conversation.getSendTarget(),
serviceId: conversation.getSendTarget(),
options: sendOptions,
proto,
timestamp,

View file

@ -11,6 +11,7 @@ import {
import { wrapWithSyncMessageSend } from '../../util/wrapWithSyncMessageSend';
import * as Bytes from '../../Bytes';
import { strictAssert } from '../../util/assert';
import { isNotNil } from '../../util/isNotNil';
import { ourProfileKeyService } from '../../services/ourProfileKey';
import type { ConversationModel } from '../../models/conversations';
@ -19,7 +20,7 @@ import type {
GroupUpdateJobData,
ConversationQueueJobBundle,
} from '../conversationJobQueue';
import { getUntrustedConversationUuids } from './getUntrustedConversationUuids';
import { getUntrustedConversationServiceIds } from './getUntrustedConversationServiceIds';
import { sendToGroup } from '../../util/sendToGroup';
// 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 recipients = jobRecipients.filter(id => {
const recipients = jobRecipients
.map(id => {
const recipient = window.ConversationController.get(id);
if (!recipient) {
return false;
return undefined;
}
if (recipient.isUnregistered()) {
log.warn(
`${logId}: dropping unregistered recipient ${recipient.idForLogging()}`
);
return false;
return undefined;
}
if (recipient.isBlocked()) {
log.warn(
`${logId}: dropping blocked recipient ${recipient.idForLogging()}`
);
return false;
return undefined;
}
return true;
});
return recipient.getSendTarget();
})
.filter(isNotNil);
const untrustedUuids = getUntrustedConversationUuids(recipients);
if (untrustedUuids.length) {
const untrustedServiceIds = getUntrustedConversationServiceIds(recipients);
if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification({
conversationId: conversation.id,
untrustedUuids,
untrustedServiceIds,
});
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 { sendToGroup } from '../../util/sendToGroup';
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';
const LONG_ATTACHMENT_LIMIT = 2048;
@ -131,29 +132,29 @@ export async function sendNormalMessage(
try {
const {
allRecipientIdentifiers,
recipientIdentifiersWithoutMe,
sentRecipientIdentifiers,
untrustedUuids,
allRecipientServiceIds,
recipientServiceIdsWithoutMe,
sentRecipientServiceIds,
untrustedServiceIds,
} = getMessageRecipients({
log,
message,
conversation,
});
if (untrustedUuids.length) {
if (untrustedServiceIds.length) {
window.reduxActions.conversations.conversationStoppedByMissingVerification(
{
conversationId: conversation.id,
untrustedUuids,
untrustedServiceIds,
}
);
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(
`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>;
if (recipientIdentifiersWithoutMe.length === 0) {
if (recipientServiceIdsWithoutMe.length === 0) {
if (
!isMe(conversation.attributes) &&
!isGroup(conversation.attributes) &&
sentRecipientIdentifiers.length === 0
sentRecipientServiceIds.length === 0
) {
log.info(
'No recipients; not sending to ourselves or to group, and no successful sends. Failing job.'
@ -230,12 +231,12 @@ export async function sendNormalMessage(
editedMessageTimestamp,
expireTimer,
groupV2: conversation.getGroupV2Info({
members: recipientIdentifiersWithoutMe,
members: recipientServiceIdsWithoutMe,
}),
preview,
profileKey,
quote,
recipients: allRecipientIdentifiers,
recipients: allRecipientServiceIds,
sticker,
storyContext,
timestamp: messageTimestamp,
@ -255,7 +256,7 @@ export async function sendNormalMessage(
}
const groupV2Info = conversation.getGroupV2Info({
members: recipientIdentifiersWithoutMe,
members: recipientServiceIdsWithoutMe,
});
if (groupV2Info && isNumber(revision)) {
groupV2Info.revision = revision;
@ -321,7 +322,7 @@ export async function sendNormalMessage(
}
log.info('sending direct message');
innerPromise = messaging.sendMessageToIdentifier({
innerPromise = messaging.sendMessageToServiceId({
attachments,
bodyRanges,
contact,
@ -330,7 +331,7 @@ export async function sendNormalMessage(
editedMessageTimestamp,
expireTimer,
groupId: undefined,
identifier: recipientIdentifiersWithoutMe[0],
serviceId: recipientServiceIdsWithoutMe[0],
messageText: body,
options: sendOptions,
preview,
@ -405,15 +406,15 @@ function getMessageRecipients({
conversation: ConversationModel;
message: MessageModel;
}>): {
allRecipientIdentifiers: Array<string>;
recipientIdentifiersWithoutMe: Array<string>;
sentRecipientIdentifiers: Array<string>;
untrustedUuids: Array<UUIDStringType>;
allRecipientServiceIds: Array<ServiceIdString>;
recipientServiceIdsWithoutMe: Array<ServiceIdString>;
sentRecipientServiceIds: Array<ServiceIdString>;
untrustedServiceIds: Array<ServiceIdString>;
} {
const allRecipientIdentifiers: Array<string> = [];
const recipientIdentifiersWithoutMe: Array<string> = [];
const untrustedUuids: Array<UUIDStringType> = [];
const sentRecipientIdentifiers: Array<string> = [];
const allRecipientServiceIds: Array<ServiceIdString> = [];
const recipientServiceIdsWithoutMe: Array<ServiceIdString> = [];
const untrustedServiceIds: Array<ServiceIdString> = [];
const sentRecipientServiceIds: Array<ServiceIdString> = [];
const currentConversationRecipients = conversation.getMemberConversationIds();
@ -436,14 +437,14 @@ function getMessageRecipients({
}
if (recipient.isUntrusted()) {
const uuid = recipient.get('uuid');
if (!uuid) {
const serviceId = recipient.getServiceId();
if (!serviceId) {
log.error(
`sendNormalMessage/getMessageRecipients: Untrusted conversation ${recipient.idForLogging()} missing UUID.`
`sendNormalMessage/getMessageRecipients: Untrusted conversation ${recipient.idForLogging()} missing serviceId.`
);
return;
}
untrustedUuids.push(uuid);
untrustedServiceIds.push(serviceId);
return;
}
if (recipient.isUnregistered()) {
@ -459,22 +460,22 @@ function getMessageRecipients({
}
if (isSent(sendState.status)) {
sentRecipientIdentifiers.push(recipientIdentifier);
sentRecipientServiceIds.push(recipientIdentifier);
return;
}
allRecipientIdentifiers.push(recipientIdentifier);
allRecipientServiceIds.push(recipientIdentifier);
if (!isRecipientMe) {
recipientIdentifiersWithoutMe.push(recipientIdentifier);
recipientServiceIdsWithoutMe.push(recipientIdentifier);
}
}
);
return {
allRecipientIdentifiers,
recipientIdentifiersWithoutMe,
sentRecipientIdentifiers,
untrustedUuids,
allRecipientServiceIds,
recipientServiceIdsWithoutMe,
sentRecipientServiceIds,
untrustedServiceIds,
};
}
@ -554,10 +555,23 @@ async function getMessageSendData({
// Save message after uploading attachments
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 storySourceUuid = storyMessage?.get('sourceUuid');
let reactionForSend: ReactionType | undefined;
if (storyReaction) {
const { targetAuthorUuid: targetAuthorAci, ...restOfReaction } =
storyReaction;
reactionForSend = {
...restOfReaction,
targetAuthorAci,
remove: false,
};
}
return {
attachments: [
@ -573,17 +587,17 @@ async function getMessageSendData({
messageTimestamp,
preview,
quote,
reaction: storyReaction
? {
...storyReaction,
remove: false,
}
: undefined,
reaction: reactionForSend,
sticker,
storyMessage,
storyContext: storyMessage
? {
authorUuid: storyMessage.get('sourceUuid'),
authorAci: storySourceUuid
? normalizeAci(
storySourceUuid,
'sendNormalMessage.storyContext.authorAci'
)
: undefined,
timestamp: storyMessage.get('sent_at'),
}
: undefined,
@ -702,7 +716,12 @@ async function uploadMessageQuote(
return {
isGiftBadge: loadedQuote.isGiftBadge,
id: loadedQuote.id,
authorUuid: loadedQuote.authorUuid,
authorAci: loadedQuote.authorUuid
? normalizeAci(
loadedQuote.authorUuid,
'sendNormalMessage.quote.authorUuid'
)
: undefined,
text: loadedQuote.text,
bodyRanges: loadedQuote.bodyRanges,
attachments: attachmentsAfterThumbnailUpload,
@ -919,7 +938,7 @@ async function markMessageFailed(
message.markFailed();
void message.saveErrors(errors, { skipSave: true });
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(
messaging.sendIndividualProto({
contentHint,
identifier: conversation.getSendTarget(),
serviceId: conversation.getSendTarget(),
options: sendOptions,
proto,
timestamp,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -103,6 +103,13 @@ export class SingleProtoJobQueue extends JobQueue<SingleProtoJobData> {
);
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 options = await getSendOptions(conversation.attributes, {
@ -118,7 +125,7 @@ export class SingleProtoJobQueue extends JobQueue<SingleProtoJobData> {
await handleMessageSend(
messaging.sendIndividualProto({
contentHint,
identifier,
serviceId,
options,
proto,
timestamp,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -8,7 +8,7 @@ import type {
MessageAttributesType,
QuotedMessageType,
} from '../model-types.d';
import type { UUIDStringType } from '../types/UUID';
import type { ServiceIdString } from '../types/ServiceId';
import { PaymentEventKind } from '../types/Payment';
import type { AnyPaymentEvent } from '../types/Payment';
import type { LocalizerType } from '../types/Util';
@ -193,7 +193,7 @@ export function getSourceDevice(
export function getSourceUuid(
message: Pick<MessageAttributesType, 'type' | 'sourceUuid'>
): UUIDStringType | undefined {
): ServiceIdString | undefined {
if (isIncoming(message) || isStory(message)) {
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 =>

View file

@ -5,7 +5,7 @@ import { get, isEmpty } from 'lodash';
import { getOwn } from '../util/getOwn';
import { map, concat, repeat, zipObject } from '../util/iterables';
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 {
SendActionType,
@ -13,6 +13,11 @@ import {
SendStatus,
} from './MessageSendState';
type LegacyCustomError = Error & {
identifier?: string;
number?: string;
};
/**
* This converts legacy message fields, such as `sent_to`, into the new
* `sendStateByConversationId` format. These legacy fields aren't typed to prevent their
@ -130,7 +135,7 @@ export function migrateLegacySendAttributes(
}
function getConversationIdsFromErrors(
errors: undefined | ReadonlyArray<CustomError>,
errors: undefined | ReadonlyArray<LegacyCustomError>,
getConversation: GetConversationType
): 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 { isNotNil } from '../util/isNotNil';
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';
const MAX_CONCURRENCY = 5;
@ -33,7 +33,7 @@ export async function migrateMessageData({
) => Promise<Array<MessageAttributesType>>;
saveMessages: (
data: ReadonlyArray<MessageAttributesType>,
options: { ourUuid: UUIDStringType }
options: { ourAci: AciString }
) => Promise<void>;
maxVersion?: number;
}>): Promise<
@ -103,7 +103,7 @@ export async function migrateMessageData({
const saveStartTime = Date.now();
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString();
const ourAci = window.textsecure.storage.user.getCheckedAci();
await saveMessages(
[
...upgradedMessages,
@ -114,7 +114,7 @@ export async function migrateMessageData({
schemaMigrationAttempts: (message.schemaMigrationAttempts ?? 0) + 1,
})),
],
{ ourUuid }
{ ourAci }
);
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 { DraftBodyRanges, RawBodyRange } from './types/BodyRange';
import type { CustomColorType, ConversationColorType } from './types/Colors';
import type { DeviceType } from './textsecure/Types.d';
import type { SendMessageChallengeData } from './textsecure/Errors';
import type { MessageModel } from './models/messages';
import type { ConversationModel } from './models/conversations';
@ -22,7 +21,8 @@ import type { AttachmentDraftType, AttachmentType } from './types/Attachment';
import type { EmbeddedContactType } from './types/EmbeddedContact';
import { SignalService as Proto } from './protobuf';
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 { SeenStatus } from './MessageSeenStatus';
import type { GiftBadgeStates } from './components/conversation/Message';
@ -47,14 +47,20 @@ export type LastMessageStatus =
| 'read'
| 'viewed';
export type SenderKeyDeviceType = {
id: number;
identifier: string;
registrationId: number;
};
export type SenderKeyInfoType = {
createdAtDate: number;
distributionId: string;
memberDevices: Array<DeviceType>;
memberDevices: Array<SenderKeyDeviceType>;
};
export type CustomError = Error & {
identifier?: string;
serviceId?: ServiceIdString;
number?: string;
data?: object;
retryAfter?: number;
@ -113,7 +119,7 @@ export type GroupV1Update = {
export type MessageReactionType = {
emoji: undefined | string;
fromId: string;
targetAuthorUuid: string;
targetAuthorUuid: AciString;
targetTimestamp: number;
timestamp: number;
isSentByConversationId?: Record<string, boolean>;
@ -164,7 +170,7 @@ export type MessageAttributesType = {
requiredProtocolVersion?: number;
retryOptions?: RetryOptions;
sourceDevice?: number;
storyDistributionListId?: string;
storyDistributionListId?: StoryDistributionIdString;
storyId?: string;
storyReplyContext?: StoryReplyContextType;
storyRecipientsVersion?: number;
@ -203,7 +209,7 @@ export type MessageAttributesType = {
conversationId: string;
storyReaction?: {
emoji: string;
targetAuthorUuid: string;
targetAuthorUuid: AciString;
targetTimestamp: number;
};
giftBadge?: {
@ -218,7 +224,7 @@ export type MessageAttributesType = {
expireTimer?: DurationInSeconds;
fromSync?: unknown;
source?: string;
sourceUuid?: string;
sourceUuid?: ServiceIdString;
};
conversationMerge?: {
renderInfo: ConversationRenderInfoType;
@ -242,7 +248,7 @@ export type MessageAttributesType = {
serverGuid?: string;
serverTimestamp?: number;
source?: string;
sourceUuid?: UUIDStringType;
sourceUuid?: ServiceIdString;
timestamp: number;
@ -371,8 +377,8 @@ export type ConversationAttributesType = {
version: number;
// Private core info
uuid?: UUIDStringType;
pni?: UUIDStringType;
uuid?: ServiceIdString;
pni?: PniString;
e164?: string;
// Private other fields
@ -463,7 +469,7 @@ export type ConversationRenderInfoType = Pick<
>;
export type GroupV2MemberType = {
uuid: UUIDStringType;
uuid: AciString;
role: MemberRoleEnum;
joinedAtVersion: number;
@ -475,19 +481,19 @@ export type GroupV2MemberType = {
};
export type GroupV2PendingMemberType = {
addedByUserId?: UUIDStringType;
uuid: UUIDStringType;
addedByUserId?: AciString;
uuid: ServiceIdString;
timestamp: number;
role: MemberRoleEnum;
};
export type GroupV2BannedMemberType = {
uuid: UUIDStringType;
uuid: ServiceIdString;
timestamp: number;
};
export type GroupV2PendingAdminApprovalType = {
uuid: UUIDStringType;
uuid: AciString;
timestamp: number;
};
@ -510,7 +516,7 @@ export type ReactionAttributesType = {
// Necessary to put 1:1 story replies into the right conversation - not the same
// conversation as the target message!
storyReactionMessage?: MessageModel;
targetAuthorUuid: string;
targetAuthorUuid: AciString;
targetTimestamp: number;
timestamp: number;
};

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,8 @@ import {
pick,
union,
} from 'lodash';
import { v4 as generateUuid } from 'uuid';
import type {
CustomError,
MessageAttributesType,
@ -40,10 +42,10 @@ import { SendMessageProtoError } from '../textsecure/Errors';
import * as expirationTimer from '../util/expirationTimer';
import { getUserLanguages } from '../util/userLanguages';
import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp';
import { getTaggedConversationUuid } from '../util/getConversationUuid';
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 Stickers from '../types/Stickers';
import * as Errors from '../types/errors';
@ -170,7 +172,6 @@ import {
queueUpdateMessage,
saveNewMessageBatcher,
} from '../util/messageBatcher';
import { normalizeUuid } from '../util/normalizeUuid';
import { getCallHistorySelector } from '../state/selectors/callHistory';
import { getConversationSelector } from '../state/selectors/conversations';
@ -343,7 +344,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
shouldSave?: boolean;
} = {}
): Promise<void> {
const ourUuid = window.textsecure.storage.user.getCheckedUuid().toString();
const ourAci = window.textsecure.storage.user.getCheckedAci();
const storyId = this.get('storyId');
if (!storyId) {
return;
@ -377,7 +378,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
},
});
if (shouldSave) {
await window.Signal.Data.saveMessage(this.attributes, { ourUuid });
await window.Signal.Data.saveMessage(this.attributes, { ourAci });
}
return;
}
@ -396,7 +397,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
},
});
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, {
i18n: window.i18n,
ourACI: window.textsecure.storage.user
.getCheckedUuid(UUIDKind.ACI)
.toString(),
ourPNI: window.textsecure.storage.user
.getCheckedUuid(UUIDKind.PNI)
.toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
ourPni: window.textsecure.storage.user.getCheckedPni(),
renderContact: (conversationId: string) => {
const conversation =
window.ConversationController.get(conversationId);
@ -849,13 +846,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
});
}
const ourUuid = window.textsecure.storage.user
.getCheckedUuid()
.toString();
const ourAci = window.textsecure.storage.user.getCheckedAci();
if (
attributes.type === 'incoming' &&
attributes.storyReaction.targetAuthorUuid === ourUuid
attributes.storyReaction.targetAuthorUuid === ourAci
) {
return window.i18n('icu:Quote__story-reaction-notification--incoming', {
emoji: attributes.storyReaction.emoji,
@ -1149,7 +1144,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (shouldPersist) {
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) {
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 => {
await window.Signal.Data.saveMessage(this.attributes, {
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 => {
await window.Signal.Data.saveMessage(this.attributes, {
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 =>
window.ConversationController.getConversationId(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
e.identifier || e.number!
e.serviceId || e.number!
) === incomingConversationId &&
(e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
@ -1509,7 +1504,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (!this.doNotSave) {
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;
// Capture successful sends
const successfulIdentifiers: Array<string> =
const successfulServiceIds: Array<ServiceIdString> =
sendIsFinal &&
'successfulIdentifiers' in result.value &&
Array.isArray(result.value.successfulIdentifiers)
? result.value.successfulIdentifiers
'successfulServiceIds' in result.value &&
Array.isArray(result.value.successfulServiceIds)
? result.value.successfulServiceIds
: [];
const sentToAtLeastOneRecipient =
result.success || Boolean(successfulIdentifiers.length);
result.success || Boolean(successfulServiceIds.length);
successfulIdentifiers.forEach(identifier => {
const conversation = window.ConversationController.get(identifier);
successfulServiceIds.forEach(serviceId => {
const conversation = window.ConversationController.get(serviceId);
if (!conversation) {
return;
}
@ -1588,7 +1583,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
errors.forEach(error => {
const conversation =
window.ConversationController.get(error.identifier) ||
window.ConversationController.get(error.serviceId) ||
window.ConversationController.get(error.number);
if (conversation && !saveErrors && sendIsFinal) {
@ -1663,7 +1658,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (!this.doNotSave) {
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;
} finally {
await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
});
if (updateLeftPane) {
@ -1809,7 +1804,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
...encodedContent,
timestamp,
destination: conv.get('e164'),
destinationUuid: getTaggedConversationUuid(conv.attributes),
destinationServiceId: conv.getServiceId(),
expirationStartTimestamp:
this.get('expirationStartTimestamp') || null,
conversationIdsSentTo,
@ -1856,7 +1851,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}
await window.Signal.Data.saveMessage(this.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
});
return result;
});
@ -2078,7 +2073,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
);
originalMessage.set(upgradedMessage);
await window.Signal.Data.saveMessage(upgradedMessage, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
});
}
} catch (error) {
@ -2206,17 +2201,15 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
: [];
unidentifiedStatus.forEach(
({ destinationUuid, destination, unidentified }) => {
const identifier =
destinationUuid?.aci || destinationUuid?.pni || destination;
({ destinationServiceId, destination, unidentified }) => {
const identifier = destinationServiceId || destination;
if (!identifier) {
return;
}
const { conversation: destinationConversation } =
window.ConversationController.maybeMergeContacts({
aci: destinationUuid?.aci,
pni: destinationUuid?.pni,
const destinationConversation =
window.ConversationController.lookupOrCreate({
uuid: destinationServiceId,
e164: destination || undefined,
reason: `handleDataMessage(${initialMessage.timestamp})`,
});
@ -2255,7 +2248,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
unidentifiedDeliveries: [...unidentifiedDeliveriesSet],
});
await window.Signal.Data.saveMessage(toUpdate.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
});
confirm();
@ -2346,9 +2339,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}
}
const ourACI = window.textsecure.storage.user.getCheckedUuid(
UUIDKind.ACI
);
const ourAci = window.textsecure.storage.user.getCheckedAci();
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const sender = window.ConversationController.lookupOrCreate({
e164: source,
@ -2371,7 +2362,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}
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
// after applying the message's associated group changes.
@ -2379,8 +2370,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
type === 'incoming' &&
!isDirectConversation(conversation.attributes) &&
hasGroupV2Prop &&
(!areWeMember ||
(sourceUuid && !conversation.hasMember(new UUID(sourceUuid))))
(!areWeMember || (sourceUuid && !conversation.hasMember(sourceUuid)))
) {
log.warn(
`${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
if (
conversation.get('announcementsOnly') &&
!conversation.isAdmin(UUID.checkedLookup(sender?.id))
) {
if (conversation.get('announcementsOnly')) {
const senderServiceId = sender.getServiceId();
if (!senderServiceId || !conversation.isAdmin(senderServiceId)) {
confirm();
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
// and not for messages from unaccepted conversations
@ -2451,7 +2441,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
if (storyContext) {
storyContextLogId =
`storyContext(${storyContext.sentTimestamp}, ` +
`${storyContext.authorUuid})`;
`${storyContext.authorAci})`;
}
const [quote, storyQuotes] = await Promise.all([
@ -2466,7 +2456,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const storyQuoteIsFromSelf =
candidateQuote.get('sourceUuid') ===
window.storage.user.getCheckedUuid().toString();
window.storage.user.getCheckedAci();
if (!storyQuoteIsFromSelf) {
return true;
@ -2581,13 +2571,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
);
}
const ourPNI = window.textsecure.storage.user.getCheckedUuid(
UUIDKind.PNI
);
const ourUuids: Set<string> = new Set([
ourACI.toString(),
ourPNI.toString(),
]);
const ourPni = window.textsecure.storage.user.getCheckedPni();
const ourUuids: Set<ServiceIdString> = new Set([ourAci, ourPni]);
message.set({
id: messageId,
@ -2609,7 +2594,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
return false;
}
return ourUuids.has(
normalizeUuid(
normalizeServiceId(
bodyRange.mentionUuid,
'handleDataMessage: mentionsMe check'
)
@ -2737,8 +2722,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
const { profileKey } = initialMessage;
if (
source === window.textsecure.storage.user.getNumber() ||
sourceUuid ===
window.textsecure.storage.user.getUuid()?.toString()
sourceUuid === window.textsecure.storage.user.getAci()
) {
conversation.set({ profileSharing: true });
} 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
await window.Signal.Data.saveMessage(generatedMessage.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
forceSave: true,
});
@ -3112,7 +3096,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
await window.Signal.Data.removeReactionFromConversation({
emoji: reaction.get('emoji'),
fromId: reaction.get('fromId'),
targetAuthorUuid: reaction.get('targetAuthorUuid'),
targetAuthorServiceId: reaction.get('targetAuthorUuid'),
targetTimestamp: reaction.get('targetTimestamp'),
});
} else {
@ -3182,7 +3166,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
shouldSave: false,
});
await window.Signal.Data.saveMessage(generatedMessage.attributes, {
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
forceSave: true,
});
@ -3216,7 +3200,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
);
await window.Signal.Data.saveMessage(this.attributes, {
jobToInsert,
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
ourAci: window.textsecure.storage.user.getCheckedAci(),
});
});
} else {
@ -3224,7 +3208,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}
} else if (shouldPersist && !isStory(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 { render } from 'react-dom';
import { Emojify } from '../../components/conversation/Emojify';
import { normalizeAci } from '../../types/ServiceId';
import type { MentionBlotValue } from '../util';
declare class QuillEmbed extends Parchment.Embed {
@ -40,7 +41,7 @@ export class MentionBlot extends Embed {
}
return {
uuid,
uuid: normalizeAci(uuid, 'quill mention blot'),
title,
};
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ import type {
GetProfileOptionsType,
GetProfileUnauthOptionsType,
} from '../textsecure/WebAPI';
import type { UUID } from '../types/UUID';
import type { ServiceIdString } from '../types/ServiceId';
import * as log from '../logging/log';
import * as Errors from '../types/errors';
import * as Bytes from '../Bytes';
@ -234,7 +234,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
const profileKey = c.get('profileKey');
const profileKeyVersion = c.deriveProfileKeyVersion();
const uuid = c.getCheckedUuid('getProfile');
const serviceId = c.getCheckedServiceId('getProfile');
const lastProfile = c.get('lastProfile');
let profileCredentialRequestContext:
@ -268,7 +268,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
context: profileCredentialRequestContext,
} = generateProfileKeyCredentialRequest(
clientZkProfileCipher,
uuid.toString(),
serviceId,
profileKey
));
@ -304,7 +304,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
try {
if (getProfileOptions.accessKey) {
try {
profile = await messaging.getProfile(uuid, getProfileOptions);
profile = await messaging.getProfile(serviceId, getProfileOptions);
} catch (error) {
if (!(error instanceof HTTPError)) {
throw error;
@ -332,7 +332,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
// We won't get the credential, but lets either fetch:
// - a versioned profile using last known profileKeyVersion
// - some basic profile information (capabilities, badges, etc).
profile = await messaging.getProfile(uuid, getProfileOptions);
profile = await messaging.getProfile(serviceId, getProfileOptions);
} catch (error) {
if (error instanceof HTTPError && error.code === 404) {
log.info(`getProfile: failed to find a profile for ${idForLogging}`);
@ -348,7 +348,7 @@ async function doGetProfile(c: ConversationModel): Promise<void> {
}
if (profile.identityKey) {
await updateIdentityKey(profile.identityKey, uuid);
await updateIdentityKey(profile.identityKey, serviceId);
}
// 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(
identityKey: string,
uuid: UUID
serviceId: ServiceIdString
): Promise<void> {
if (!identityKey) {
return;
@ -582,17 +582,17 @@ export async function updateIdentityKey(
const identityKeyBytes = Bytes.fromBase64(identityKey);
const changed = await window.textsecure.storage.protocol.saveIdentity(
new Address(uuid, 1),
new Address(serviceId, 1),
identityKeyBytes,
false
);
if (changed) {
log.info(`updateIdentityKey(${uuid.toString()}): changed`);
log.info(`updateIdentityKey(${serviceId}): changed`);
// save identity will close all sessions except for .1, so we
// 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(
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
// SPDX-License-Identifier: AGPL-3.0-only
import { v4 as generateUuid } from 'uuid';
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.
// 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
// other parts of the app via its referencing UUID.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const promises = new Map<UUIDStringType, ExplodePromiseResultType<any>>();
const promises = new Map<
SingleServePromiseIdString,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
ExplodePromiseResultType<any>
>();
export function set<T>(
explodedPromise: ExplodePromiseResultType<T>
): UUIDStringType {
let uuid = UUID.generate().toString();
): SingleServePromiseIdString {
let uuid = generateUuid() as SingleServePromiseIdString;
while (promises.has(uuid)) {
uuid = UUID.generate().toString();
uuid = generateUuid() as SingleServePromiseIdString;
}
promises.set(uuid, {
@ -38,7 +43,7 @@ export function set<T>(
}
export function get<T>(
uuid: UUIDStringType
uuid: SingleServePromiseIdString
): ExplodePromiseResultType<T> | undefined {
return promises.get(uuid);
}

View file

@ -1512,7 +1512,7 @@ async function processRemoteRecords(
// Find remote contact records that:
// - 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>();
prunedStorageItems = prunedStorageItems.filter(item => {
const { itemType, storageRecord } = item;
@ -1521,18 +1521,12 @@ async function processRemoteRecords(
return true;
}
if (
!contact.serviceE164 ||
!contact.pni ||
contact.pni !== contact.serviceUuid
) {
if (!contact.serviceE164 || !contact.pni) {
return true;
}
const localUuid = window.ConversationController.get(contact.pni)?.get(
'uuid'
);
if (!localUuid || localUuid === contact.pni) {
const localAci = window.ConversationController.get(contact.pni)?.getAci();
if (!localAci) {
return true;
}

View file

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

View file

@ -172,7 +172,7 @@ async function repairUnexpiredStories(): Promise<void> {
await Promise.all(
storiesWithExpiry.map(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 VisualAttachment from './types/VisualAttachment';
import * as MessageType from './types/Message2';
import { UUID } from './types/UUID';
import { Address } from './types/Address';
import { QualifiedAddress } from './types/QualifiedAddress';
@ -388,7 +387,6 @@ export const setup = (options: {
Message: MessageType,
// Mostly for debugging
UUID,
Address,
QualifiedAddress,
};

View file

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

View file

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

View file

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

View file

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

View file

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

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