Import/export gift badges, other fields
This commit is contained in:
parent
af1c593fef
commit
e6b62001d3
10 changed files with 536 additions and 55 deletions
|
@ -349,6 +349,7 @@ message ChatItem {
|
||||||
RemoteDeletedMessage remoteDeletedMessage = 14;
|
RemoteDeletedMessage remoteDeletedMessage = 14;
|
||||||
ChatUpdateMessage updateMessage = 15;
|
ChatUpdateMessage updateMessage = 15;
|
||||||
PaymentNotification paymentNotification = 16;
|
PaymentNotification paymentNotification = 16;
|
||||||
|
GiftBadge giftBadge = 17;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +440,18 @@ message PaymentNotification {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GiftBadge {
|
||||||
|
enum State {
|
||||||
|
UNOPENED = 0;
|
||||||
|
OPENED = 1;
|
||||||
|
REDEEMED = 2;
|
||||||
|
FAILED = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes receiptCredentialPresentation = 1;
|
||||||
|
State state = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message ContactAttachment {
|
message ContactAttachment {
|
||||||
message Name {
|
message Name {
|
||||||
optional string givenName = 1;
|
optional string givenName = 1;
|
||||||
|
|
|
@ -195,6 +195,7 @@ message AccountRecord {
|
||||||
optional bytes subscriberId = 21;
|
optional bytes subscriberId = 21;
|
||||||
optional string subscriberCurrencyCode = 22;
|
optional string subscriberCurrencyCode = 22;
|
||||||
optional bool displayBadgesOnProfile = 23;
|
optional bool displayBadgesOnProfile = 23;
|
||||||
|
optional bool donorSubscriptionManuallyCancelled = 24;
|
||||||
optional bool keepMutedChatsArchived = 25;
|
optional bool keepMutedChatsArchived = 25;
|
||||||
optional bool hasSetMyStoriesPrivacy = 26;
|
optional bool hasSetMyStoriesPrivacy = 26;
|
||||||
optional bool hasViewedOnboardingStory = 27;
|
optional bool hasViewedOnboardingStory = 27;
|
||||||
|
@ -202,12 +203,13 @@ message AccountRecord {
|
||||||
optional bool storiesDisabled = 29;
|
optional bool storiesDisabled = 29;
|
||||||
optional OptionalBool storyViewReceiptsEnabled = 30;
|
optional OptionalBool storyViewReceiptsEnabled = 30;
|
||||||
reserved 31; // hasReadOnboardingStory
|
reserved 31; // hasReadOnboardingStory
|
||||||
reserved 32; // hasSeenGroupStoryEducationSheet
|
optional bool hasSeenGroupStoryEducationSheet = 32;
|
||||||
optional string username = 33;
|
optional string username = 33;
|
||||||
optional bool hasCompletedUsernameOnboarding = 34;
|
optional bool hasCompletedUsernameOnboarding = 34;
|
||||||
optional UsernameLink usernameLink = 35;
|
optional UsernameLink usernameLink = 35;
|
||||||
optional bytes backupsSubscriberId = 36;
|
optional bytes backupsSubscriberId = 36;
|
||||||
optional string backupsSubscriberCurrencyCode = 37;
|
optional string backupsSubscriberCurrencyCode = 37;
|
||||||
|
optional bool backupsSubscriptionManuallyCancelled = 38;
|
||||||
}
|
}
|
||||||
|
|
||||||
message StoryDistributionListRecord {
|
message StoryDistributionListRecord {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { Backups, SignalService } from '../../protobuf';
|
||||||
import Data from '../../sql/Client';
|
import Data from '../../sql/Client';
|
||||||
import type { PageMessagesCursorType } from '../../sql/Interface';
|
import type { PageMessagesCursorType } from '../../sql/Interface';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
|
import { GiftBadgeStates } from '../../components/conversation/Message';
|
||||||
import { StorySendMode, MY_STORY_ID } from '../../types/Stories';
|
import { StorySendMode, MY_STORY_ID } from '../../types/Stories';
|
||||||
import {
|
import {
|
||||||
isPniString,
|
isPniString,
|
||||||
|
@ -478,12 +479,20 @@ export class BackupExportStream extends Readable {
|
||||||
? {
|
? {
|
||||||
subscriberId: backupsSubscriberId,
|
subscriberId: backupsSubscriberId,
|
||||||
currencyCode: storage.get('backupsSubscriberCurrencyCode'),
|
currencyCode: storage.get('backupsSubscriberCurrencyCode'),
|
||||||
|
manuallyCancelled: storage.get(
|
||||||
|
'backupsSubscriptionManuallyCancelled',
|
||||||
|
false
|
||||||
|
),
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
donationSubscriberData: Bytes.isNotEmpty(subscriberId)
|
donationSubscriberData: Bytes.isNotEmpty(subscriberId)
|
||||||
? {
|
? {
|
||||||
subscriberId,
|
subscriberId,
|
||||||
currencyCode: storage.get('subscriberCurrencyCode'),
|
currencyCode: storage.get('subscriberCurrencyCode'),
|
||||||
|
manuallyCancelled: storage.get(
|
||||||
|
'donorSubscriptionManuallyCancelled',
|
||||||
|
false
|
||||||
|
),
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
accountSettings: {
|
accountSettings: {
|
||||||
|
@ -507,6 +516,9 @@ export class BackupExportStream extends Readable {
|
||||||
hasCompletedUsernameOnboarding: storage.get(
|
hasCompletedUsernameOnboarding: storage.get(
|
||||||
'hasCompletedUsernameOnboarding'
|
'hasCompletedUsernameOnboarding'
|
||||||
),
|
),
|
||||||
|
hasSeenGroupStoryEducationSheet: storage.get(
|
||||||
|
'hasSeenGroupStoryEducationSheet'
|
||||||
|
),
|
||||||
phoneNumberSharingMode,
|
phoneNumberSharingMode,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -849,6 +861,31 @@ export class BackupExportStream extends Readable {
|
||||||
sticker: stickerProto,
|
sticker: stickerProto,
|
||||||
reactions: this.getMessageReactions(message),
|
reactions: this.getMessageReactions(message),
|
||||||
};
|
};
|
||||||
|
} else if (isGiftBadge(message)) {
|
||||||
|
const { giftBadge } = message;
|
||||||
|
strictAssert(giftBadge != null, 'Message must have gift badge');
|
||||||
|
|
||||||
|
let state: Backups.GiftBadge.State;
|
||||||
|
switch (giftBadge.state) {
|
||||||
|
case GiftBadgeStates.Unopened:
|
||||||
|
state = Backups.GiftBadge.State.UNOPENED;
|
||||||
|
break;
|
||||||
|
case GiftBadgeStates.Opened:
|
||||||
|
state = Backups.GiftBadge.State.OPENED;
|
||||||
|
break;
|
||||||
|
case GiftBadgeStates.Redeemed:
|
||||||
|
state = Backups.GiftBadge.State.REDEEMED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw missingCaseError(giftBadge.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.giftBadge = {
|
||||||
|
receiptCredentialPresentation: Bytes.fromBase64(
|
||||||
|
giftBadge.receiptCredentialPresentation
|
||||||
|
),
|
||||||
|
state,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
result.standardMessage = await this.toStandardMessage(
|
result.standardMessage = await this.toStandardMessage(
|
||||||
message,
|
message,
|
||||||
|
@ -1187,10 +1224,6 @@ export class BackupExportStream extends Readable {
|
||||||
return { kind: NonBubbleResultKind.Drop };
|
return { kind: NonBubbleResultKind.Drop };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isGiftBadge(message)) {
|
|
||||||
// TODO (DESKTOP-6964): reuse quote's handling
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isGroupUpdate(message)) {
|
if (isGroupUpdate(message)) {
|
||||||
// GV1 is deprecated.
|
// GV1 is deprecated.
|
||||||
return { kind: NonBubbleResultKind.Drop };
|
return { kind: NonBubbleResultKind.Drop };
|
||||||
|
@ -1837,7 +1870,11 @@ export class BackupExportStream extends Readable {
|
||||||
}: Pick<MessageAttributesType, 'reactions'>):
|
}: Pick<MessageAttributesType, 'reactions'>):
|
||||||
| Array<Backups.IReaction>
|
| Array<Backups.IReaction>
|
||||||
| undefined {
|
| undefined {
|
||||||
return reactions?.map(reaction => {
|
if (reactions == null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reactions?.map((reaction, sortOrder) => {
|
||||||
return {
|
return {
|
||||||
emoji: reaction.emoji,
|
emoji: reaction.emoji,
|
||||||
authorId: this.getOrPushPrivateRecipient({
|
authorId: this.getOrPushPrivateRecipient({
|
||||||
|
@ -1847,6 +1884,7 @@ export class BackupExportStream extends Readable {
|
||||||
receivedTimestamp: getSafeLongFromTimestamp(
|
receivedTimestamp: getSafeLongFromTimestamp(
|
||||||
reaction.receivedAtDate ?? reaction.timestamp
|
reaction.receivedAtDate ?? reaction.timestamp
|
||||||
),
|
),
|
||||||
|
sortOrder: Long.fromNumber(sortOrder),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1856,12 +1894,14 @@ export class BackupExportStream extends Readable {
|
||||||
editMessageReceivedAtMs,
|
editMessageReceivedAtMs,
|
||||||
serverTimestamp,
|
serverTimestamp,
|
||||||
readStatus,
|
readStatus,
|
||||||
|
unidentifiedDeliveryReceived,
|
||||||
}: Pick<
|
}: Pick<
|
||||||
MessageAttributesType,
|
MessageAttributesType,
|
||||||
| 'received_at_ms'
|
| 'received_at_ms'
|
||||||
| 'editMessageReceivedAtMs'
|
| 'editMessageReceivedAtMs'
|
||||||
| 'serverTimestamp'
|
| 'serverTimestamp'
|
||||||
| 'readStatus'
|
| 'readStatus'
|
||||||
|
| 'unidentifiedDeliveryReceived'
|
||||||
>): Backups.ChatItem.IIncomingMessageDetails {
|
>): Backups.ChatItem.IIncomingMessageDetails {
|
||||||
const dateReceived = editMessageReceivedAtMs || receivedAtMs;
|
const dateReceived = editMessageReceivedAtMs || receivedAtMs;
|
||||||
return {
|
return {
|
||||||
|
@ -1872,6 +1912,7 @@ export class BackupExportStream extends Readable {
|
||||||
? getSafeLongFromTimestamp(serverTimestamp)
|
? getSafeLongFromTimestamp(serverTimestamp)
|
||||||
: null,
|
: null,
|
||||||
read: readStatus === ReadStatus.Read,
|
read: readStatus === ReadStatus.Read,
|
||||||
|
sealedSender: unidentifiedDeliveryReceived === true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1879,10 +1920,22 @@ export class BackupExportStream extends Readable {
|
||||||
sentAt: number,
|
sentAt: number,
|
||||||
{
|
{
|
||||||
sendStateByConversationId = {},
|
sendStateByConversationId = {},
|
||||||
}: Pick<MessageAttributesType, 'sendStateByConversationId'>
|
unidentifiedDeliveries = [],
|
||||||
|
errors = [],
|
||||||
|
}: Pick<
|
||||||
|
MessageAttributesType,
|
||||||
|
'sendStateByConversationId' | 'unidentifiedDeliveries' | 'errors'
|
||||||
|
>
|
||||||
): Backups.ChatItem.IOutgoingMessageDetails {
|
): Backups.ChatItem.IOutgoingMessageDetails {
|
||||||
const BackupSendStatus = Backups.SendStatus.Status;
|
const BackupSendStatus = Backups.SendStatus.Status;
|
||||||
|
|
||||||
|
const sealedSenderServiceIds = new Set(unidentifiedDeliveries);
|
||||||
|
const errorMap = new Map(
|
||||||
|
errors.map(({ serviceId, name }) => {
|
||||||
|
return [serviceId, name];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const sendStatus = new Array<Backups.ISendStatus>();
|
const sendStatus = new Array<Backups.ISendStatus>();
|
||||||
for (const [id, entry] of Object.entries(sendStateByConversationId)) {
|
for (const [id, entry] of Object.entries(sendStateByConversationId)) {
|
||||||
const target = window.ConversationController.get(id);
|
const target = window.ConversationController.get(id);
|
||||||
|
@ -1915,6 +1968,19 @@ export class BackupExportStream extends Readable {
|
||||||
throw missingCaseError(entry.status);
|
throw missingCaseError(entry.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { serviceId } = target.attributes;
|
||||||
|
let networkFailure = false;
|
||||||
|
let identityKeyMismatch = false;
|
||||||
|
let sealedSender = false;
|
||||||
|
if (serviceId) {
|
||||||
|
const errorName = errorMap.get(serviceId);
|
||||||
|
if (errorName !== undefined) {
|
||||||
|
identityKeyMismatch = errorName === 'OutgoingIdentityKeyError';
|
||||||
|
networkFailure = !identityKeyMismatch;
|
||||||
|
}
|
||||||
|
sealedSender = sealedSenderServiceIds.has(serviceId);
|
||||||
|
}
|
||||||
|
|
||||||
sendStatus.push({
|
sendStatus.push({
|
||||||
recipientId: this.getOrPushPrivateRecipient(target.attributes),
|
recipientId: this.getOrPushPrivateRecipient(target.attributes),
|
||||||
lastStatusUpdateTimestamp:
|
lastStatusUpdateTimestamp:
|
||||||
|
@ -1922,6 +1988,9 @@ export class BackupExportStream extends Readable {
|
||||||
? getSafeLongFromTimestamp(entry.updatedAt)
|
? getSafeLongFromTimestamp(entry.updatedAt)
|
||||||
: null,
|
: null,
|
||||||
deliveryStatus,
|
deliveryStatus,
|
||||||
|
networkFailure,
|
||||||
|
identityKeyMismatch,
|
||||||
|
sealedSender,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { Aci, Pni } from '@signalapp/libsignal-client';
|
import { Aci, Pni } from '@signalapp/libsignal-client';
|
||||||
|
import { ReceiptCredentialPresentation } from '@signalapp/libsignal-client/zkgroup';
|
||||||
import { v4 as generateUuid } from 'uuid';
|
import { v4 as generateUuid } from 'uuid';
|
||||||
import pMap from 'p-map';
|
import pMap from 'p-map';
|
||||||
import { Writable } from 'stream';
|
import { Writable } from 'stream';
|
||||||
|
@ -11,6 +12,7 @@ import { Backups, SignalService } from '../../protobuf';
|
||||||
import Data from '../../sql/Client';
|
import Data from '../../sql/Client';
|
||||||
import type { StoryDistributionWithMembersType } from '../../sql/Interface';
|
import type { StoryDistributionWithMembersType } from '../../sql/Interface';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
|
import { GiftBadgeStates } from '../../components/conversation/Message';
|
||||||
import { StorySendMode } from '../../types/Stories';
|
import { StorySendMode } from '../../types/Stories';
|
||||||
import type { ServiceIdString, AciString } from '../../types/ServiceId';
|
import type { ServiceIdString, AciString } from '../../types/ServiceId';
|
||||||
import { fromAciObject, fromPniObject } from '../../types/ServiceId';
|
import { fromAciObject, fromPniObject } from '../../types/ServiceId';
|
||||||
|
@ -27,6 +29,7 @@ import {
|
||||||
} from '../../types/Stickers';
|
} from '../../types/Stickers';
|
||||||
import type {
|
import type {
|
||||||
ConversationAttributesType,
|
ConversationAttributesType,
|
||||||
|
CustomError,
|
||||||
MessageAttributesType,
|
MessageAttributesType,
|
||||||
MessageReactionType,
|
MessageReactionType,
|
||||||
EditHistoryType,
|
EditHistoryType,
|
||||||
|
@ -34,7 +37,7 @@ import type {
|
||||||
} from '../../model-types.d';
|
} from '../../model-types.d';
|
||||||
import { assertDev, strictAssert } from '../../util/assert';
|
import { assertDev, strictAssert } from '../../util/assert';
|
||||||
import { getTimestampFromLong } from '../../util/timestampLongUtils';
|
import { getTimestampFromLong } from '../../util/timestampLongUtils';
|
||||||
import { DurationInSeconds } from '../../util/durations';
|
import { DurationInSeconds, SECOND } from '../../util/durations';
|
||||||
import { dropNull } from '../../util/dropNull';
|
import { dropNull } from '../../util/dropNull';
|
||||||
import {
|
import {
|
||||||
deriveGroupID,
|
deriveGroupID,
|
||||||
|
@ -468,22 +471,36 @@ export class BackupImportStream extends Writable {
|
||||||
await storage.put('avatarUrl', avatarUrlPath);
|
await storage.put('avatarUrl', avatarUrlPath);
|
||||||
}
|
}
|
||||||
if (donationSubscriberData != null) {
|
if (donationSubscriberData != null) {
|
||||||
const { subscriberId, currencyCode } = donationSubscriberData;
|
const { subscriberId, currencyCode, manuallyCancelled } =
|
||||||
|
donationSubscriberData;
|
||||||
if (Bytes.isNotEmpty(subscriberId)) {
|
if (Bytes.isNotEmpty(subscriberId)) {
|
||||||
await storage.put('subscriberId', subscriberId);
|
await storage.put('subscriberId', subscriberId);
|
||||||
}
|
}
|
||||||
if (currencyCode != null) {
|
if (currencyCode != null) {
|
||||||
await storage.put('subscriberCurrencyCode', currencyCode);
|
await storage.put('subscriberCurrencyCode', currencyCode);
|
||||||
}
|
}
|
||||||
|
if (manuallyCancelled != null) {
|
||||||
|
await storage.put(
|
||||||
|
'donorSubscriptionManuallyCancelled',
|
||||||
|
manuallyCancelled
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (backupsSubscriberData != null) {
|
if (backupsSubscriberData != null) {
|
||||||
const { subscriberId, currencyCode } = backupsSubscriberData;
|
const { subscriberId, currencyCode, manuallyCancelled } =
|
||||||
|
backupsSubscriberData;
|
||||||
if (Bytes.isNotEmpty(subscriberId)) {
|
if (Bytes.isNotEmpty(subscriberId)) {
|
||||||
await storage.put('backupsSubscriberId', subscriberId);
|
await storage.put('backupsSubscriberId', subscriberId);
|
||||||
}
|
}
|
||||||
if (currencyCode != null) {
|
if (currencyCode != null) {
|
||||||
await storage.put('backupsSubscriberCurrencyCode', currencyCode);
|
await storage.put('backupsSubscriberCurrencyCode', currencyCode);
|
||||||
}
|
}
|
||||||
|
if (manuallyCancelled != null) {
|
||||||
|
await storage.put(
|
||||||
|
'backupsSubscriptionManuallyCancelled',
|
||||||
|
manuallyCancelled
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await storage.put(
|
await storage.put(
|
||||||
|
@ -503,6 +520,12 @@ export class BackupImportStream extends Writable {
|
||||||
'preferContactAvatars',
|
'preferContactAvatars',
|
||||||
accountSettings?.preferContactAvatars === true
|
accountSettings?.preferContactAvatars === true
|
||||||
);
|
);
|
||||||
|
if (accountSettings?.universalExpireTimer) {
|
||||||
|
await storage.put(
|
||||||
|
'universalExpireTimer',
|
||||||
|
accountSettings.universalExpireTimer
|
||||||
|
);
|
||||||
|
}
|
||||||
await storage.put(
|
await storage.put(
|
||||||
'displayBadgesOnProfile',
|
'displayBadgesOnProfile',
|
||||||
accountSettings?.displayBadgesOnProfile === true
|
accountSettings?.displayBadgesOnProfile === true
|
||||||
|
@ -532,8 +555,8 @@ export class BackupImportStream extends Writable {
|
||||||
accountSettings?.hasCompletedUsernameOnboarding === true
|
accountSettings?.hasCompletedUsernameOnboarding === true
|
||||||
);
|
);
|
||||||
await storage.put(
|
await storage.put(
|
||||||
'preferredReactionEmoji',
|
'hasSeenGroupStoryEducationSheet',
|
||||||
accountSettings?.preferredReactionEmoji || []
|
accountSettings?.hasSeenGroupStoryEducationSheet === true
|
||||||
);
|
);
|
||||||
await storage.put(
|
await storage.put(
|
||||||
'preferredReactionEmoji',
|
'preferredReactionEmoji',
|
||||||
|
@ -618,6 +641,7 @@ export class BackupImportStream extends Writable {
|
||||||
profileName: dropNull(contact.profileGivenName),
|
profileName: dropNull(contact.profileGivenName),
|
||||||
profileFamilyName: dropNull(contact.profileFamilyName),
|
profileFamilyName: dropNull(contact.profileFamilyName),
|
||||||
hideStory: contact.hideStory === true,
|
hideStory: contact.hideStory === true,
|
||||||
|
username: dropNull(contact.username),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (contact.notRegistered) {
|
if (contact.notRegistered) {
|
||||||
|
@ -871,8 +895,6 @@ export class BackupImportStream extends Writable {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.standardMessage) {
|
if (item.standardMessage) {
|
||||||
// TODO (DESKTOP-6964): gift badge
|
|
||||||
|
|
||||||
attributes = {
|
attributes = {
|
||||||
...attributes,
|
...attributes,
|
||||||
...(await this.fromStandardMessage(item.standardMessage, chatConvo.id)),
|
...(await this.fromStandardMessage(item.standardMessage, chatConvo.id)),
|
||||||
|
@ -961,6 +983,8 @@ export class BackupImportStream extends Writable {
|
||||||
|
|
||||||
const BackupSendStatus = Backups.SendStatus.Status;
|
const BackupSendStatus = Backups.SendStatus.Status;
|
||||||
|
|
||||||
|
const unidentifiedDeliveries = new Array<ServiceIdString>();
|
||||||
|
const errors = new Array<CustomError>();
|
||||||
for (const status of outgoing.sendStatus ?? []) {
|
for (const status of outgoing.sendStatus ?? []) {
|
||||||
strictAssert(
|
strictAssert(
|
||||||
status.recipientId,
|
status.recipientId,
|
||||||
|
@ -997,6 +1021,28 @@ export class BackupImportStream extends Writable {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.serviceId) {
|
||||||
|
if (status.sealedSender) {
|
||||||
|
unidentifiedDeliveries.push(target.serviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.identityKeyMismatch) {
|
||||||
|
errors.push({
|
||||||
|
serviceId: target.serviceId,
|
||||||
|
name: 'OutgoingIdentityKeyError',
|
||||||
|
// See: ts/textsecure/Errors
|
||||||
|
message: `The identity of ${target.serviceId} has changed.`,
|
||||||
|
});
|
||||||
|
} else if (status.networkFailure) {
|
||||||
|
errors.push({
|
||||||
|
serviceId: target.serviceId,
|
||||||
|
name: 'OutgoingMessageError',
|
||||||
|
// See: ts/textsecure/Errors
|
||||||
|
message: 'no http error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sendStateByConversationId[target.id] = {
|
sendStateByConversationId[target.id] = {
|
||||||
status: sendStatus,
|
status: sendStatus,
|
||||||
updatedAt:
|
updatedAt:
|
||||||
|
@ -1011,6 +1057,10 @@ export class BackupImportStream extends Writable {
|
||||||
patch: {
|
patch: {
|
||||||
sendStateByConversationId,
|
sendStateByConversationId,
|
||||||
received_at_ms: timestamp,
|
received_at_ms: timestamp,
|
||||||
|
unidentifiedDeliveries: unidentifiedDeliveries.length
|
||||||
|
? unidentifiedDeliveries
|
||||||
|
: undefined,
|
||||||
|
errors: errors.length ? errors : undefined,
|
||||||
},
|
},
|
||||||
newActiveAt: timestamp,
|
newActiveAt: timestamp,
|
||||||
};
|
};
|
||||||
|
@ -1018,12 +1068,15 @@ export class BackupImportStream extends Writable {
|
||||||
if (incoming) {
|
if (incoming) {
|
||||||
const receivedAtMs = incoming.dateReceived?.toNumber() ?? Date.now();
|
const receivedAtMs = incoming.dateReceived?.toNumber() ?? Date.now();
|
||||||
|
|
||||||
|
const unidentifiedDeliveryReceived = incoming.sealedSender === true;
|
||||||
|
|
||||||
if (incoming.read) {
|
if (incoming.read) {
|
||||||
return {
|
return {
|
||||||
patch: {
|
patch: {
|
||||||
readStatus: ReadStatus.Read,
|
readStatus: ReadStatus.Read,
|
||||||
seenStatus: SeenStatus.Seen,
|
seenStatus: SeenStatus.Seen,
|
||||||
received_at_ms: receivedAtMs,
|
received_at_ms: receivedAtMs,
|
||||||
|
unidentifiedDeliveryReceived,
|
||||||
},
|
},
|
||||||
newActiveAt: receivedAtMs,
|
newActiveAt: receivedAtMs,
|
||||||
};
|
};
|
||||||
|
@ -1034,6 +1087,7 @@ export class BackupImportStream extends Writable {
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
received_at_ms: receivedAtMs,
|
received_at_ms: receivedAtMs,
|
||||||
|
unidentifiedDeliveryReceived,
|
||||||
},
|
},
|
||||||
newActiveAt: receivedAtMs,
|
newActiveAt: receivedAtMs,
|
||||||
unread: true,
|
unread: true,
|
||||||
|
@ -1203,8 +1257,15 @@ export class BackupImportStream extends Writable {
|
||||||
if (!reactions?.length) {
|
if (!reactions?.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return reactions.map(
|
return reactions
|
||||||
({ emoji, authorId, sentTimestamp, receivedTimestamp }) => {
|
.slice()
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (a.sortOrder && b.sortOrder) {
|
||||||
|
return a.sortOrder.comp(b.sortOrder);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
.map(({ emoji, authorId, sentTimestamp, receivedTimestamp }) => {
|
||||||
strictAssert(emoji != null, 'reaction must have an emoji');
|
strictAssert(emoji != null, 'reaction must have an emoji');
|
||||||
strictAssert(authorId != null, 'reaction must have authorId');
|
strictAssert(authorId != null, 'reaction must have authorId');
|
||||||
strictAssert(
|
strictAssert(
|
||||||
|
@ -1229,8 +1290,7 @@ export class BackupImportStream extends Writable {
|
||||||
receivedAtDate: getTimestampFromLong(receivedTimestamp),
|
receivedAtDate: getTimestampFromLong(receivedTimestamp),
|
||||||
timestamp: getTimestampFromLong(sentTimestamp),
|
timestamp: getTimestampFromLong(sentTimestamp),
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fromNonBubbleChatItem(
|
private async fromNonBubbleChatItem(
|
||||||
|
@ -1401,6 +1461,52 @@ export class BackupImportStream extends Writable {
|
||||||
additionalMessages: [],
|
additionalMessages: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
if (chatItem.giftBadge) {
|
||||||
|
const { giftBadge } = chatItem;
|
||||||
|
strictAssert(
|
||||||
|
Bytes.isNotEmpty(giftBadge.receiptCredentialPresentation),
|
||||||
|
'Gift badge must have a presentation'
|
||||||
|
);
|
||||||
|
|
||||||
|
let state: GiftBadgeStates;
|
||||||
|
switch (giftBadge.state) {
|
||||||
|
case Backups.GiftBadge.State.OPENED:
|
||||||
|
state = GiftBadgeStates.Opened;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Backups.GiftBadge.State.FAILED:
|
||||||
|
case Backups.GiftBadge.State.REDEEMED:
|
||||||
|
state = GiftBadgeStates.Redeemed;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Backups.GiftBadge.State.UNOPENED:
|
||||||
|
state = GiftBadgeStates.Unopened;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
state = GiftBadgeStates.Unopened;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const receipt = new ReceiptCredentialPresentation(
|
||||||
|
Buffer.from(giftBadge.receiptCredentialPresentation)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: {
|
||||||
|
giftBadge: {
|
||||||
|
receiptCredentialPresentation: Bytes.toBase64(
|
||||||
|
giftBadge.receiptCredentialPresentation
|
||||||
|
),
|
||||||
|
expiration: Number(receipt.getReceiptExpirationTime()) * SECOND,
|
||||||
|
id: undefined,
|
||||||
|
level: Number(receipt.getReceiptLevel()),
|
||||||
|
state,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
additionalMessages: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
if (chatItem.updateMessage) {
|
if (chatItem.updateMessage) {
|
||||||
return this.fromChatItemUpdateMessage(chatItem.updateMessage, options);
|
return this.fromChatItemUpdateMessage(chatItem.updateMessage, options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -397,6 +397,13 @@ export function toAccountRecord(
|
||||||
if (typeof subscriberCurrencyCode === 'string') {
|
if (typeof subscriberCurrencyCode === 'string') {
|
||||||
accountRecord.subscriberCurrencyCode = subscriberCurrencyCode;
|
accountRecord.subscriberCurrencyCode = subscriberCurrencyCode;
|
||||||
}
|
}
|
||||||
|
const donorSubscriptionManuallyCancelled = window.storage.get(
|
||||||
|
'donorSubscriptionManuallyCancelled'
|
||||||
|
);
|
||||||
|
if (typeof donorSubscriptionManuallyCancelled === 'boolean') {
|
||||||
|
accountRecord.donorSubscriptionManuallyCancelled =
|
||||||
|
donorSubscriptionManuallyCancelled;
|
||||||
|
}
|
||||||
const backupsSubscriberId = window.storage.get('backupsSubscriberId');
|
const backupsSubscriberId = window.storage.get('backupsSubscriberId');
|
||||||
if (Bytes.isNotEmpty(backupsSubscriberId)) {
|
if (Bytes.isNotEmpty(backupsSubscriberId)) {
|
||||||
accountRecord.backupsSubscriberId = backupsSubscriberId;
|
accountRecord.backupsSubscriberId = backupsSubscriberId;
|
||||||
|
@ -407,6 +414,13 @@ export function toAccountRecord(
|
||||||
if (typeof backupsSubscriberCurrencyCode === 'string') {
|
if (typeof backupsSubscriberCurrencyCode === 'string') {
|
||||||
accountRecord.backupsSubscriberCurrencyCode = backupsSubscriberCurrencyCode;
|
accountRecord.backupsSubscriberCurrencyCode = backupsSubscriberCurrencyCode;
|
||||||
}
|
}
|
||||||
|
const backupsSubscriptionManuallyCancelled = window.storage.get(
|
||||||
|
'backupsSubscriptionManuallyCancelled'
|
||||||
|
);
|
||||||
|
if (typeof backupsSubscriptionManuallyCancelled === 'boolean') {
|
||||||
|
accountRecord.backupsSubscriptionManuallyCancelled =
|
||||||
|
backupsSubscriptionManuallyCancelled;
|
||||||
|
}
|
||||||
const displayBadgesOnProfile = window.storage.get('displayBadgesOnProfile');
|
const displayBadgesOnProfile = window.storage.get('displayBadgesOnProfile');
|
||||||
if (displayBadgesOnProfile !== undefined) {
|
if (displayBadgesOnProfile !== undefined) {
|
||||||
accountRecord.displayBadgesOnProfile = displayBadgesOnProfile;
|
accountRecord.displayBadgesOnProfile = displayBadgesOnProfile;
|
||||||
|
@ -436,6 +450,14 @@ export function toAccountRecord(
|
||||||
hasCompletedUsernameOnboarding;
|
hasCompletedUsernameOnboarding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasSeenGroupStoryEducationSheet = window.storage.get(
|
||||||
|
'hasSeenGroupStoryEducationSheet'
|
||||||
|
);
|
||||||
|
if (hasSeenGroupStoryEducationSheet !== undefined) {
|
||||||
|
accountRecord.hasSeenGroupStoryEducationSheet =
|
||||||
|
hasSeenGroupStoryEducationSheet;
|
||||||
|
}
|
||||||
|
|
||||||
const hasStoriesDisabled = window.storage.get('hasStoriesDisabled');
|
const hasStoriesDisabled = window.storage.get('hasStoriesDisabled');
|
||||||
accountRecord.storiesDisabled = hasStoriesDisabled === true;
|
accountRecord.storiesDisabled = hasStoriesDisabled === true;
|
||||||
|
|
||||||
|
@ -1235,11 +1257,14 @@ export async function mergeAccountRecord(
|
||||||
preferredReactionEmoji: rawPreferredReactionEmoji,
|
preferredReactionEmoji: rawPreferredReactionEmoji,
|
||||||
subscriberId,
|
subscriberId,
|
||||||
subscriberCurrencyCode,
|
subscriberCurrencyCode,
|
||||||
|
donorSubscriptionManuallyCancelled,
|
||||||
backupsSubscriberId,
|
backupsSubscriberId,
|
||||||
backupsSubscriberCurrencyCode,
|
backupsSubscriberCurrencyCode,
|
||||||
|
backupsSubscriptionManuallyCancelled,
|
||||||
displayBadgesOnProfile,
|
displayBadgesOnProfile,
|
||||||
keepMutedChatsArchived,
|
keepMutedChatsArchived,
|
||||||
hasCompletedUsernameOnboarding,
|
hasCompletedUsernameOnboarding,
|
||||||
|
hasSeenGroupStoryEducationSheet,
|
||||||
hasSetMyStoriesPrivacy,
|
hasSetMyStoriesPrivacy,
|
||||||
hasViewedOnboardingStory,
|
hasViewedOnboardingStory,
|
||||||
storiesDisabled,
|
storiesDisabled,
|
||||||
|
@ -1448,6 +1473,12 @@ export async function mergeAccountRecord(
|
||||||
if (typeof subscriberCurrencyCode === 'string') {
|
if (typeof subscriberCurrencyCode === 'string') {
|
||||||
await window.storage.put('subscriberCurrencyCode', subscriberCurrencyCode);
|
await window.storage.put('subscriberCurrencyCode', subscriberCurrencyCode);
|
||||||
}
|
}
|
||||||
|
if (donorSubscriptionManuallyCancelled != null) {
|
||||||
|
await window.storage.put(
|
||||||
|
'donorSubscriptionManuallyCancelled',
|
||||||
|
donorSubscriptionManuallyCancelled
|
||||||
|
);
|
||||||
|
}
|
||||||
if (Bytes.isNotEmpty(backupsSubscriberId)) {
|
if (Bytes.isNotEmpty(backupsSubscriberId)) {
|
||||||
await window.storage.put('backupsSubscriberId', backupsSubscriberId);
|
await window.storage.put('backupsSubscriberId', backupsSubscriberId);
|
||||||
}
|
}
|
||||||
|
@ -1457,6 +1488,12 @@ export async function mergeAccountRecord(
|
||||||
backupsSubscriberCurrencyCode
|
backupsSubscriberCurrencyCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (backupsSubscriptionManuallyCancelled != null) {
|
||||||
|
await window.storage.put(
|
||||||
|
'backupsSubscriptionManuallyCancelled',
|
||||||
|
backupsSubscriptionManuallyCancelled
|
||||||
|
);
|
||||||
|
}
|
||||||
await window.storage.put(
|
await window.storage.put(
|
||||||
'displayBadgesOnProfile',
|
'displayBadgesOnProfile',
|
||||||
Boolean(displayBadgesOnProfile)
|
Boolean(displayBadgesOnProfile)
|
||||||
|
@ -1490,6 +1527,15 @@ export async function mergeAccountRecord(
|
||||||
hasCompletedUsernameOnboardingBool
|
hasCompletedUsernameOnboardingBool
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
const hasCompletedUsernameOnboardingBool = Boolean(
|
||||||
|
hasSeenGroupStoryEducationSheet
|
||||||
|
);
|
||||||
|
await window.storage.put(
|
||||||
|
'hasSeenGroupStoryEducationSheet',
|
||||||
|
hasCompletedUsernameOnboardingBool
|
||||||
|
);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
const hasStoriesDisabled = Boolean(storiesDisabled);
|
const hasStoriesDisabled = Boolean(storiesDisabled);
|
||||||
await window.storage.put('hasStoriesDisabled', hasStoriesDisabled);
|
await window.storage.put('hasStoriesDisabled', hasStoriesDisabled);
|
||||||
|
|
|
@ -118,6 +118,7 @@ describe('backup/attachments', () => {
|
||||||
timestamp,
|
timestamp,
|
||||||
readStatus: ReadStatus.Read,
|
readStatus: ReadStatus.Read,
|
||||||
seenStatus: SeenStatus.Seen,
|
seenStatus: SeenStatus.Seen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
...overrides,
|
...overrides,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { v4 as generateGuid } from 'uuid';
|
||||||
|
|
||||||
import { SendStatus } from '../../messages/MessageSendState';
|
import { SendStatus } from '../../messages/MessageSendState';
|
||||||
import type { ConversationModel } from '../../models/conversations';
|
import type { ConversationModel } from '../../models/conversations';
|
||||||
|
import { GiftBadgeStates } from '../../components/conversation/Message';
|
||||||
|
|
||||||
import Data from '../../sql/Client';
|
import Data from '../../sql/Client';
|
||||||
import { generateAci } from '../../types/ServiceId';
|
import { generateAci } from '../../types/ServiceId';
|
||||||
|
@ -15,6 +16,15 @@ import { setupBasics, symmetricRoundtripHarness, OUR_ACI } from './helpers';
|
||||||
|
|
||||||
const CONTACT_A = generateAci();
|
const CONTACT_A = generateAci();
|
||||||
|
|
||||||
|
const BADGE_RECEIPT =
|
||||||
|
'AEpyZxbRBT+T5PQw9Wcx1QE2aFvL7LoLir9V4UF09Kk9qiP4SpIlHdlWHrAICy6F' +
|
||||||
|
'6WdbdCj45fY6cadDKbBmkw+abohRTJnItrFhyKurnA5X+mZHZv4OvS+aZFmAYS6J' +
|
||||||
|
'W+hpkbI+Fk7Gu3mEix7Pgz1I2EwGFlUBpm7/nuD5A0cKLrUJAMM142fnOEervePV' +
|
||||||
|
'bf0c6Sw5X5aCsBw9J+dxFUGAAAAAAAAAAMH58UUeUj2oH1jfqc0Hb2RUtdA3ee8X' +
|
||||||
|
'0Pp83WT8njwFw5rNGSHeKqOvBZzfAhMGJoiz7l1XfIfsPIreaFb/tA9aq2bOAdDl' +
|
||||||
|
'5OYlxxl6DnjQ3+g3k9ycpl0elkaQnPW2Ai7yjeJ/96K1qssR2a/2b7xi10dmTRGg' +
|
||||||
|
'gebhZnroYYgIgK22ZgAAAABkAAAAAAAAAD9j4f77Xo2Ox5tVyrV2DUo=';
|
||||||
|
|
||||||
describe('backup/bubble messages', () => {
|
describe('backup/bubble messages', () => {
|
||||||
let contactA: ConversationModel;
|
let contactA: ConversationModel;
|
||||||
|
|
||||||
|
@ -48,6 +58,7 @@ describe('backup/bubble messages', () => {
|
||||||
body: 'd',
|
body: 'd',
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
editMessageTimestamp: 5,
|
editMessageTimestamp: 5,
|
||||||
editMessageReceivedAtMs: 5,
|
editMessageReceivedAtMs: 5,
|
||||||
editHistory: [
|
editHistory: [
|
||||||
|
@ -89,6 +100,7 @@ describe('backup/bubble messages', () => {
|
||||||
status: SendStatus.Delivered,
|
status: SendStatus.Delivered,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
unidentifiedDeliveries: [CONTACT_A],
|
||||||
timestamp: 3,
|
timestamp: 3,
|
||||||
editMessageTimestamp: 5,
|
editMessageTimestamp: 5,
|
||||||
editMessageReceivedAtMs: 5,
|
editMessageReceivedAtMs: 5,
|
||||||
|
@ -131,4 +143,224 @@ describe('backup/bubble messages', () => {
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.skip('roundtrips unopened gift badge', async () => {
|
||||||
|
await symmetricRoundtripHarness([
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'incoming',
|
||||||
|
received_at: 3,
|
||||||
|
received_at_ms: 3,
|
||||||
|
sent_at: 3,
|
||||||
|
sourceServiceId: CONTACT_A,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
|
timestamp: 3,
|
||||||
|
giftBadge: {
|
||||||
|
id: undefined,
|
||||||
|
level: 100,
|
||||||
|
expiration: 1723248000000,
|
||||||
|
receiptCredentialPresentation: BADGE_RECEIPT,
|
||||||
|
state: GiftBadgeStates.Opened,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('roundtrips opened gift badge', async () => {
|
||||||
|
await symmetricRoundtripHarness([
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'incoming',
|
||||||
|
received_at: 3,
|
||||||
|
received_at_ms: 3,
|
||||||
|
sent_at: 3,
|
||||||
|
sourceServiceId: CONTACT_A,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
|
timestamp: 3,
|
||||||
|
giftBadge: {
|
||||||
|
id: undefined,
|
||||||
|
level: 100,
|
||||||
|
expiration: 1723248000000,
|
||||||
|
receiptCredentialPresentation: BADGE_RECEIPT,
|
||||||
|
state: GiftBadgeStates.Opened,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it.skip('roundtrips gift badge quote', async () => {
|
||||||
|
await symmetricRoundtripHarness([
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'incoming',
|
||||||
|
received_at: 3,
|
||||||
|
received_at_ms: 3,
|
||||||
|
sent_at: 3,
|
||||||
|
sourceServiceId: CONTACT_A,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
|
timestamp: 3,
|
||||||
|
giftBadge: {
|
||||||
|
id: undefined,
|
||||||
|
level: 100,
|
||||||
|
expiration: 1723248000000,
|
||||||
|
receiptCredentialPresentation: BADGE_RECEIPT,
|
||||||
|
state: GiftBadgeStates.Opened,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'incoming',
|
||||||
|
received_at: 4,
|
||||||
|
received_at_ms: 4,
|
||||||
|
sent_at: 4,
|
||||||
|
sourceServiceId: CONTACT_A,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
|
timestamp: 4,
|
||||||
|
quote: {
|
||||||
|
authorAci: CONTACT_A,
|
||||||
|
attachments: [],
|
||||||
|
id: 3,
|
||||||
|
isViewOnce: false,
|
||||||
|
isGiftBadge: true,
|
||||||
|
messageId: '',
|
||||||
|
referencedMessageNotFound: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('roundtrips sealed/unsealed incoming message', async () => {
|
||||||
|
await symmetricRoundtripHarness([
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'incoming',
|
||||||
|
received_at: 3,
|
||||||
|
received_at_ms: 3,
|
||||||
|
sent_at: 3,
|
||||||
|
sourceServiceId: CONTACT_A,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: false,
|
||||||
|
timestamp: 3,
|
||||||
|
body: 'unsealed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'incoming',
|
||||||
|
received_at: 4,
|
||||||
|
received_at_ms: 4,
|
||||||
|
sent_at: 4,
|
||||||
|
sourceServiceId: CONTACT_A,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
|
timestamp: 4,
|
||||||
|
body: 'sealed',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('roundtrips sealed/unsealed outgoing message', async () => {
|
||||||
|
await symmetricRoundtripHarness([
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'outgoing',
|
||||||
|
received_at: 3,
|
||||||
|
received_at_ms: 3,
|
||||||
|
sent_at: 3,
|
||||||
|
sourceServiceId: OUR_ACI,
|
||||||
|
sendStateByConversationId: {
|
||||||
|
[contactA.id]: {
|
||||||
|
status: SendStatus.Delivered,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unidentifiedDeliveries: undefined,
|
||||||
|
timestamp: 3,
|
||||||
|
body: 'unsealed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'outgoing',
|
||||||
|
received_at: 4,
|
||||||
|
received_at_ms: 4,
|
||||||
|
sent_at: 4,
|
||||||
|
sourceServiceId: OUR_ACI,
|
||||||
|
sendStateByConversationId: {
|
||||||
|
[contactA.id]: {
|
||||||
|
status: SendStatus.Delivered,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
unidentifiedDeliveries: [CONTACT_A],
|
||||||
|
timestamp: 4,
|
||||||
|
body: 'sealed',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('roundtrips messages with send errors', async () => {
|
||||||
|
await symmetricRoundtripHarness([
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'outgoing',
|
||||||
|
received_at: 3,
|
||||||
|
received_at_ms: 3,
|
||||||
|
sent_at: 3,
|
||||||
|
sourceServiceId: OUR_ACI,
|
||||||
|
sendStateByConversationId: {
|
||||||
|
[contactA.id]: {
|
||||||
|
status: SendStatus.Delivered,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
serviceId: CONTACT_A,
|
||||||
|
name: 'OutgoingIdentityKeyError',
|
||||||
|
message: `The identity of ${CONTACT_A} has changed.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timestamp: 3,
|
||||||
|
body: 'body',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conversationId: contactA.id,
|
||||||
|
id: generateGuid(),
|
||||||
|
type: 'outgoing',
|
||||||
|
received_at: 4,
|
||||||
|
received_at_ms: 4,
|
||||||
|
sent_at: 4,
|
||||||
|
sourceServiceId: OUR_ACI,
|
||||||
|
sendStateByConversationId: {
|
||||||
|
[contactA.id]: {
|
||||||
|
status: SendStatus.Delivered,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
errors: [
|
||||||
|
{
|
||||||
|
serviceId: CONTACT_A,
|
||||||
|
name: 'OutgoingMessageError',
|
||||||
|
message: 'no http error',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
timestamp: 4,
|
||||||
|
body: 'body',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -93,7 +93,9 @@ function sortAndNormalize(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// Get rid of unserializable `undefined` values.
|
||||||
|
return JSON.parse(
|
||||||
|
JSON.stringify({
|
||||||
...rest,
|
...rest,
|
||||||
conversationId: mapConvoId(conversationId),
|
conversationId: mapConvoId(conversationId),
|
||||||
reactions: reactions?.map(({ fromId, ...restOfReaction }) => {
|
reactions: reactions?.map(({ fromId, ...restOfReaction }) => {
|
||||||
|
@ -122,7 +124,8 @@ function sortAndNormalize(
|
||||||
|
|
||||||
// Not an original property, but useful
|
// Not an original property, but useful
|
||||||
isUnsupported: isUnsupportedMessage(message),
|
isUnsupported: isUnsupportedMessage(message),
|
||||||
};
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
sourceDevice: 1,
|
sourceDevice: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
flags: Proto.DataMessage.Flags.END_SESSION,
|
flags: Proto.DataMessage.Flags.END_SESSION,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -204,6 +205,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
timestamp: 1,
|
timestamp: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -224,12 +226,12 @@ describe('backup/non-bubble messages', () => {
|
||||||
timestamp: 1,
|
timestamp: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: DESKTOP-7122
|
it('roundtrips bare payments notification', async () => {
|
||||||
it.skip('roundtrips bare payments notification', async () => {
|
|
||||||
await symmetricRoundtripHarness([
|
await symmetricRoundtripHarness([
|
||||||
{
|
{
|
||||||
conversationId: contactA.id,
|
conversationId: contactA.id,
|
||||||
|
@ -243,6 +245,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
sourceDevice: 1,
|
sourceDevice: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
payment: {
|
payment: {
|
||||||
kind: PaymentEventKind.Notification,
|
kind: PaymentEventKind.Notification,
|
||||||
note: 'note with text',
|
note: 'note with text',
|
||||||
|
@ -251,8 +254,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: DESKTOP-7122
|
it('roundtrips full payments notification', async () => {
|
||||||
it.skip('roundtrips full payments notification', async () => {
|
|
||||||
await symmetricRoundtripHarness([
|
await symmetricRoundtripHarness([
|
||||||
{
|
{
|
||||||
conversationId: contactA.id,
|
conversationId: contactA.id,
|
||||||
|
@ -266,6 +268,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
sourceDevice: 1,
|
sourceDevice: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
payment: {
|
payment: {
|
||||||
kind: PaymentEventKind.Notification,
|
kind: PaymentEventKind.Notification,
|
||||||
note: 'note with text',
|
note: 'note with text',
|
||||||
|
@ -297,6 +300,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
sourceDevice: 1,
|
sourceDevice: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
contact: [
|
contact: [
|
||||||
{
|
{
|
||||||
name: {
|
name: {
|
||||||
|
@ -339,6 +343,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
sourceDevice: 1,
|
sourceDevice: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
// TODO (DESKTOP-6845): properly handle data FilePointer
|
// TODO (DESKTOP-6845): properly handle data FilePointer
|
||||||
sticker: {
|
sticker: {
|
||||||
emoji: '👍',
|
emoji: '👍',
|
||||||
|
@ -373,6 +378,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
sourceDevice: 1,
|
sourceDevice: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
isErased: true,
|
isErased: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
@ -475,8 +481,7 @@ describe('backup/non-bubble messages', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: DESKTOP-7122
|
it('roundtrips unsupported message', async () => {
|
||||||
it.skip('roundtrips unsupported message', async () => {
|
|
||||||
await symmetricRoundtripHarness([
|
await symmetricRoundtripHarness([
|
||||||
{
|
{
|
||||||
conversationId: contactA.id,
|
conversationId: contactA.id,
|
||||||
|
@ -490,8 +495,9 @@ describe('backup/non-bubble messages', () => {
|
||||||
timestamp: 1,
|
timestamp: 1,
|
||||||
readStatus: ReadStatus.Unread,
|
readStatus: ReadStatus.Unread,
|
||||||
seenStatus: SeenStatus.Unseen,
|
seenStatus: SeenStatus.Unseen,
|
||||||
supportedVersionAtReceive: 1,
|
unidentifiedDeliveryReceived: true,
|
||||||
requiredProtocolVersion: 2,
|
supportedVersionAtReceive: 5,
|
||||||
|
requiredProtocolVersion: 6,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
3
ts/types/Storage.d.ts
vendored
3
ts/types/Storage.d.ts
vendored
|
@ -72,6 +72,7 @@ export type StorageAccessType = {
|
||||||
hasCompletedUsernameOnboarding: boolean;
|
hasCompletedUsernameOnboarding: boolean;
|
||||||
hasCompletedUsernameLinkOnboarding: boolean;
|
hasCompletedUsernameLinkOnboarding: boolean;
|
||||||
hasCompletedSafetyNumberOnboarding: boolean;
|
hasCompletedSafetyNumberOnboarding: boolean;
|
||||||
|
hasSeenGroupStoryEducationSheet: boolean;
|
||||||
hasViewedOnboardingStory: boolean;
|
hasViewedOnboardingStory: boolean;
|
||||||
hasStoriesDisabled: boolean;
|
hasStoriesDisabled: boolean;
|
||||||
storyViewReceiptsEnabled: boolean;
|
storyViewReceiptsEnabled: boolean;
|
||||||
|
@ -154,8 +155,10 @@ export type StorageAccessType = {
|
||||||
areWeASubscriber: boolean;
|
areWeASubscriber: boolean;
|
||||||
subscriberId: Uint8Array;
|
subscriberId: Uint8Array;
|
||||||
subscriberCurrencyCode: string;
|
subscriberCurrencyCode: string;
|
||||||
|
donorSubscriptionManuallyCancelled: boolean;
|
||||||
backupsSubscriberId: Uint8Array;
|
backupsSubscriberId: Uint8Array;
|
||||||
backupsSubscriberCurrencyCode: string;
|
backupsSubscriberCurrencyCode: string;
|
||||||
|
backupsSubscriptionManuallyCancelled: boolean;
|
||||||
displayBadgesOnProfile: boolean;
|
displayBadgesOnProfile: boolean;
|
||||||
keepMutedChatsArchived: boolean;
|
keepMutedChatsArchived: boolean;
|
||||||
usernameLastIntegrityCheck: number;
|
usernameLastIntegrityCheck: number;
|
||||||
|
|
Loading…
Reference in a new issue