Introduce Service Id Types
Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
parent
414c0a58d3
commit
366b875fd2
269 changed files with 5832 additions and 5550 deletions
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
@ -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
222
ts/background.ts
222
ts/background.ts
|
@ -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,
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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(),
|
||||
}),
|
||||
},
|
||||
],
|
||||
|
|
|
@ -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 })],
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -358,7 +358,9 @@ export function ForwardMessagesModal({
|
|||
toggleSelectedConversation(conversationId);
|
||||
}
|
||||
}}
|
||||
lookupConversationWithoutUuid={asyncShouldNeverBeCalled}
|
||||
lookupConversationWithoutServiceId={
|
||||
asyncShouldNeverBeCalled
|
||||
}
|
||||
showConversation={shouldNeverBeCalled}
|
||||
showUserNotFoundModal={shouldNeverBeCalled}
|
||||
setIsFetchingUUID={shouldNeverBeCalled}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
|
|
@ -115,7 +115,9 @@ export const SingleList = Template.bind({});
|
|||
},
|
||||
{
|
||||
...fakeDistroList,
|
||||
members: fakeDistroList.memberUuids.map(() => getDefaultConversation()),
|
||||
members: fakeDistroList.memberServiceIds.map(() =>
|
||||
getDefaultConversation()
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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',
|
||||
},
|
||||
|
|
|
@ -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',
|
||||
};
|
||||
|
|
|
@ -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
|
||||
/>
|
||||
|
|
|
@ -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 =>
|
||||
|
|
|
@ -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
|
||||
/>
|
||||
|
|
|
@ -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(),
|
||||
},
|
||||
})),
|
||||
],
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 });
|
||||
|
||||
|
|
|
@ -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;
|
||||
}>;
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
645
ts/groups.ts
645
ts/groups.ts
File diff suppressed because it is too large
Load diff
|
@ -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`
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ export async function sendNullMessage(
|
|||
await handleMessageSend(
|
||||
messaging.sendIndividualProto({
|
||||
contentHint,
|
||||
identifier: conversation.getSendTarget(),
|
||||
serviceId: conversation.getSendTarget(),
|
||||
options: sendOptions,
|
||||
proto,
|
||||
timestamp,
|
||||
|
|
|
@ -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`
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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`
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =>
|
||||
|
|
|
@ -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> = [];
|
||||
|
|
|
@ -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
40
ts/model-types.d.ts
vendored
|
@ -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
|
@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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>;
|
||||
|
|
138
ts/sql/Server.ts
138
ts/sql/Server.ts
|
@ -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,
|
||||
})
|
||||
);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue