Fix more import/export tests for backups
This commit is contained in:
parent
8dabe4fbe4
commit
84c562d0b2
13 changed files with 225 additions and 60 deletions
|
@ -196,14 +196,22 @@ export enum GiftBadgeStates {
|
||||||
Unopened = 'Unopened',
|
Unopened = 'Unopened',
|
||||||
Opened = 'Opened',
|
Opened = 'Opened',
|
||||||
Redeemed = 'Redeemed',
|
Redeemed = 'Redeemed',
|
||||||
|
Failed = 'Failed',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GiftBadgeType = {
|
export type GiftBadgeType =
|
||||||
expiration: number;
|
| {
|
||||||
id: string | undefined;
|
state:
|
||||||
level: number;
|
| GiftBadgeStates.Unopened
|
||||||
state: GiftBadgeStates;
|
| GiftBadgeStates.Opened
|
||||||
};
|
| GiftBadgeStates.Redeemed;
|
||||||
|
expiration: number;
|
||||||
|
id: string | undefined;
|
||||||
|
level: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
state: GiftBadgeStates.Failed;
|
||||||
|
};
|
||||||
|
|
||||||
export type PropsData = {
|
export type PropsData = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -1385,7 +1393,10 @@ export class Message extends React.PureComponent<Props, State> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (giftBadge.state === GiftBadgeStates.Unopened) {
|
if (
|
||||||
|
giftBadge.state === GiftBadgeStates.Unopened ||
|
||||||
|
giftBadge.state === GiftBadgeStates.Failed
|
||||||
|
) {
|
||||||
const description =
|
const description =
|
||||||
direction === 'incoming'
|
direction === 'incoming'
|
||||||
? i18n('icu:message--donation--unopened--incoming')
|
? i18n('icu:message--donation--unopened--incoming')
|
||||||
|
|
|
@ -2009,6 +2009,13 @@ GiftBadgeUnopened.args = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const GiftBadgeFailed = Template.bind({});
|
||||||
|
GiftBadgeFailed.args = {
|
||||||
|
giftBadge: {
|
||||||
|
state: GiftBadgeStates.Failed,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const getPreferredBadge = () => ({
|
const getPreferredBadge = () => ({
|
||||||
category: BadgeCategory.Donor,
|
category: BadgeCategory.Donor,
|
||||||
descriptionTemplate: 'This is a description of the badge',
|
descriptionTemplate: 'This is a description of the badge',
|
||||||
|
|
|
@ -138,7 +138,7 @@ export async function onSync(sync: ViewSyncAttributesType): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const giftBadge = message.get('giftBadge');
|
const giftBadge = message.get('giftBadge');
|
||||||
if (giftBadge) {
|
if (giftBadge && giftBadge.state !== GiftBadgeStates.Failed) {
|
||||||
didChangeMessage = true;
|
didChangeMessage = true;
|
||||||
message.set({
|
message.set({
|
||||||
giftBadge: {
|
giftBadge: {
|
||||||
|
|
24
ts/model-types.d.ts
vendored
24
ts/model-types.d.ts
vendored
|
@ -136,6 +136,9 @@ export type EditHistoryType = {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
received_at: number;
|
received_at: number;
|
||||||
received_at_ms?: number;
|
received_at_ms?: number;
|
||||||
|
serverTimestamp?: number;
|
||||||
|
readStatus?: ReadStatus;
|
||||||
|
unidentifiedDeliveryReceived?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MessageType =
|
type MessageType =
|
||||||
|
@ -225,13 +228,20 @@ export type MessageAttributesType = {
|
||||||
targetAuthorAci: AciString;
|
targetAuthorAci: AciString;
|
||||||
targetTimestamp: number;
|
targetTimestamp: number;
|
||||||
};
|
};
|
||||||
giftBadge?: {
|
giftBadge?:
|
||||||
expiration: number;
|
| {
|
||||||
level: number;
|
state:
|
||||||
id: string | undefined;
|
| GiftBadgeStates.Unopened
|
||||||
receiptCredentialPresentation: string;
|
| GiftBadgeStates.Opened
|
||||||
state: GiftBadgeStates;
|
| GiftBadgeStates.Redeemed;
|
||||||
};
|
expiration: number;
|
||||||
|
level: number;
|
||||||
|
id: string | undefined;
|
||||||
|
receiptCredentialPresentation: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
state: GiftBadgeStates.Failed;
|
||||||
|
};
|
||||||
|
|
||||||
expirationTimerUpdate?: {
|
expirationTimerUpdate?: {
|
||||||
expireTimer?: DurationInSeconds;
|
expireTimer?: DurationInSeconds;
|
||||||
|
|
|
@ -158,6 +158,7 @@ import {
|
||||||
} from '../messages/copyQuote';
|
} from '../messages/copyQuote';
|
||||||
import { getRoomIdFromCallLink } from '../util/callLinksRingrtc';
|
import { getRoomIdFromCallLink } from '../util/callLinksRingrtc';
|
||||||
import { explodePromise } from '../util/explodePromise';
|
import { explodePromise } from '../util/explodePromise';
|
||||||
|
import { GiftBadgeStates } from '../components/conversation/Message';
|
||||||
|
|
||||||
/* eslint-disable more/no-then */
|
/* eslint-disable more/no-then */
|
||||||
|
|
||||||
|
@ -2076,7 +2077,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
await DataWriter.updateConversation(conversation.attributes);
|
await DataWriter.updateConversation(conversation.attributes);
|
||||||
|
|
||||||
const giftBadge = message.get('giftBadge');
|
const giftBadge = message.get('giftBadge');
|
||||||
if (giftBadge) {
|
if (giftBadge && giftBadge.state !== GiftBadgeStates.Failed) {
|
||||||
const { level } = giftBadge;
|
const { level } = giftBadge;
|
||||||
const { updatesUrl } = window.SignalContext.config;
|
const { updatesUrl } = window.SignalContext.config;
|
||||||
strictAssert(
|
strictAssert(
|
||||||
|
|
|
@ -22,6 +22,7 @@ import * as log from '../../logging/log';
|
||||||
import { GiftBadgeStates } from '../../components/conversation/Message';
|
import { GiftBadgeStates } from '../../components/conversation/Message';
|
||||||
import { type CustomColorType } from '../../types/Colors';
|
import { type CustomColorType } from '../../types/Colors';
|
||||||
import { StorySendMode, MY_STORY_ID } from '../../types/Stories';
|
import { StorySendMode, MY_STORY_ID } from '../../types/Stories';
|
||||||
|
import { getStickerPacksForBackup } from '../../types/Stickers';
|
||||||
import {
|
import {
|
||||||
isPniString,
|
isPniString,
|
||||||
type AciString,
|
type AciString,
|
||||||
|
@ -384,7 +385,7 @@ export class BackupExportStream extends Readable {
|
||||||
stats.callLinks += 1;
|
stats.callLinks += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stickerPacks = await DataReader.getInstalledStickerPacks();
|
const stickerPacks = await getStickerPacksForBackup();
|
||||||
|
|
||||||
for (const { id, key } of stickerPacks) {
|
for (const { id, key } of stickerPacks) {
|
||||||
this.pushFrame({
|
this.pushFrame({
|
||||||
|
@ -1142,27 +1143,33 @@ export class BackupExportStream extends Readable {
|
||||||
const { giftBadge } = message;
|
const { giftBadge } = message;
|
||||||
strictAssert(giftBadge != null, 'Message must have gift badge');
|
strictAssert(giftBadge != null, 'Message must have gift badge');
|
||||||
|
|
||||||
let state: Backups.GiftBadge.State;
|
if (giftBadge.state === GiftBadgeStates.Failed) {
|
||||||
switch (giftBadge.state) {
|
result.giftBadge = {
|
||||||
case GiftBadgeStates.Unopened:
|
state: Backups.GiftBadge.State.FAILED,
|
||||||
state = Backups.GiftBadge.State.UNOPENED;
|
};
|
||||||
break;
|
} else {
|
||||||
case GiftBadgeStates.Opened:
|
let state: Backups.GiftBadge.State;
|
||||||
state = Backups.GiftBadge.State.OPENED;
|
switch (giftBadge.state) {
|
||||||
break;
|
case GiftBadgeStates.Unopened:
|
||||||
case GiftBadgeStates.Redeemed:
|
state = Backups.GiftBadge.State.UNOPENED;
|
||||||
state = Backups.GiftBadge.State.REDEEMED;
|
break;
|
||||||
break;
|
case GiftBadgeStates.Opened:
|
||||||
default:
|
state = Backups.GiftBadge.State.OPENED;
|
||||||
throw missingCaseError(giftBadge.state);
|
break;
|
||||||
}
|
case GiftBadgeStates.Redeemed:
|
||||||
|
state = Backups.GiftBadge.State.REDEEMED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw missingCaseError(giftBadge);
|
||||||
|
}
|
||||||
|
|
||||||
result.giftBadge = {
|
result.giftBadge = {
|
||||||
receiptCredentialPresentation: Bytes.fromBase64(
|
receiptCredentialPresentation: Bytes.fromBase64(
|
||||||
giftBadge.receiptCredentialPresentation
|
giftBadge.receiptCredentialPresentation
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
result.standardMessage = await this.toStandardMessage(
|
result.standardMessage = await this.toStandardMessage(
|
||||||
message,
|
message,
|
||||||
|
|
|
@ -35,7 +35,8 @@ import {
|
||||||
import {
|
import {
|
||||||
STICKERPACK_ID_BYTE_LEN,
|
STICKERPACK_ID_BYTE_LEN,
|
||||||
STICKERPACK_KEY_BYTE_LEN,
|
STICKERPACK_KEY_BYTE_LEN,
|
||||||
downloadStickerPack,
|
createPacksFromBackup,
|
||||||
|
type StickerPackPointerType,
|
||||||
} from '../../types/Stickers';
|
} from '../../types/Stickers';
|
||||||
import type {
|
import type {
|
||||||
ConversationColorType,
|
ConversationColorType,
|
||||||
|
@ -285,6 +286,7 @@ export class BackupImportStream extends Writable {
|
||||||
return processMessagesBatch(ourAci, batch);
|
return processMessagesBatch(ourAci, batch);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
private readonly stickerPacks = new Array<StickerPackPointerType>();
|
||||||
private ourConversation?: ConversationAttributesType;
|
private ourConversation?: ConversationAttributesType;
|
||||||
private pinnedConversations = new Array<[number, string]>();
|
private pinnedConversations = new Array<[number, string]>();
|
||||||
private customColorById = new Map<number, CustomColorDataType>();
|
private customColorById = new Map<number, CustomColorDataType>();
|
||||||
|
@ -357,6 +359,9 @@ export class BackupImportStream extends Writable {
|
||||||
await this.conversationOpBatcher.flushAndWait();
|
await this.conversationOpBatcher.flushAndWait();
|
||||||
await this.saveMessageBatcher.flushAndWait();
|
await this.saveMessageBatcher.flushAndWait();
|
||||||
|
|
||||||
|
// Store sticker packs and schedule downloads
|
||||||
|
await createPacksFromBackup(this.stickerPacks);
|
||||||
|
|
||||||
// Reset and reload conversations and storage again
|
// Reset and reload conversations and storage again
|
||||||
window.ConversationController.reset();
|
window.ConversationController.reset();
|
||||||
|
|
||||||
|
@ -1537,8 +1542,14 @@ export class BackupImportStream extends Writable {
|
||||||
const timestamp = getTimestampFromLong(rev.dateSent);
|
const timestamp = getTimestampFromLong(rev.dateSent);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
// eslint-disable-next-line camelcase
|
patch: {
|
||||||
patch: { sendStateByConversationId, received_at_ms },
|
sendStateByConversationId,
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
received_at_ms,
|
||||||
|
serverTimestamp,
|
||||||
|
readStatus,
|
||||||
|
unidentifiedDeliveryReceived,
|
||||||
|
},
|
||||||
} = this.fromDirectionDetails(rev, timestamp);
|
} = this.fromDirectionDetails(rev, timestamp);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1551,6 +1562,9 @@ export class BackupImportStream extends Writable {
|
||||||
sendStateByConversationId,
|
sendStateByConversationId,
|
||||||
// eslint-disable-next-line camelcase
|
// eslint-disable-next-line camelcase
|
||||||
received_at_ms,
|
received_at_ms,
|
||||||
|
serverTimestamp,
|
||||||
|
readStatus,
|
||||||
|
unidentifiedDeliveryReceived,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
// Fix order: from newest to oldest
|
// Fix order: from newest to oldest
|
||||||
|
@ -1572,6 +1586,9 @@ export class BackupImportStream extends Writable {
|
||||||
timestamp: mainMessage.timestamp,
|
timestamp: mainMessage.timestamp,
|
||||||
received_at: mainMessage.received_at,
|
received_at: mainMessage.received_at,
|
||||||
received_at_ms: mainMessage.received_at_ms,
|
received_at_ms: mainMessage.received_at_ms,
|
||||||
|
serverTimestamp: mainMessage.serverTimestamp,
|
||||||
|
readStatus: mainMessage.readStatus,
|
||||||
|
unidentifiedDeliveryReceived: mainMessage.unidentifiedDeliveryReceived,
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1863,6 +1880,17 @@ export class BackupImportStream extends Writable {
|
||||||
}
|
}
|
||||||
if (chatItem.giftBadge) {
|
if (chatItem.giftBadge) {
|
||||||
const { giftBadge } = chatItem;
|
const { giftBadge } = chatItem;
|
||||||
|
if (giftBadge.state === Backups.GiftBadge.State.FAILED) {
|
||||||
|
return {
|
||||||
|
message: {
|
||||||
|
giftBadge: {
|
||||||
|
state: GiftBadgeStates.Failed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
additionalMessages: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
strictAssert(
|
strictAssert(
|
||||||
Bytes.isNotEmpty(giftBadge.receiptCredentialPresentation),
|
Bytes.isNotEmpty(giftBadge.receiptCredentialPresentation),
|
||||||
'Gift badge must have a presentation'
|
'Gift badge must have a presentation'
|
||||||
|
@ -1874,15 +1902,11 @@ export class BackupImportStream extends Writable {
|
||||||
state = GiftBadgeStates.Opened;
|
state = GiftBadgeStates.Opened;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Backups.GiftBadge.State.FAILED:
|
|
||||||
case Backups.GiftBadge.State.REDEEMED:
|
case Backups.GiftBadge.State.REDEEMED:
|
||||||
state = GiftBadgeStates.Redeemed;
|
state = GiftBadgeStates.Redeemed;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Backups.GiftBadge.State.UNOPENED:
|
case Backups.GiftBadge.State.UNOPENED:
|
||||||
state = GiftBadgeStates.Unopened;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
state = GiftBadgeStates.Unopened;
|
state = GiftBadgeStates.Unopened;
|
||||||
break;
|
break;
|
||||||
|
@ -2842,25 +2866,23 @@ export class BackupImportStream extends Writable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fromStickerPack({
|
private async fromStickerPack({
|
||||||
packId: id,
|
packId: packIdBytes,
|
||||||
packKey: key,
|
packKey: packKeyBytes,
|
||||||
}: Backups.IStickerPack): Promise<void> {
|
}: Backups.IStickerPack): Promise<void> {
|
||||||
strictAssert(
|
strictAssert(
|
||||||
id?.length === STICKERPACK_ID_BYTE_LEN,
|
packIdBytes?.length === STICKERPACK_ID_BYTE_LEN,
|
||||||
'Sticker pack must have a valid pack id'
|
'Sticker pack must have a valid pack id'
|
||||||
);
|
);
|
||||||
|
|
||||||
const logId = `fromStickerPack(${Bytes.toHex(id).slice(-2)})`;
|
const id = Bytes.toHex(packIdBytes);
|
||||||
|
const logId = `fromStickerPack(${id.slice(-2)})`;
|
||||||
strictAssert(
|
strictAssert(
|
||||||
key?.length === STICKERPACK_KEY_BYTE_LEN,
|
packKeyBytes?.length === STICKERPACK_KEY_BYTE_LEN,
|
||||||
`${logId}: must have a valid pack key`
|
`${logId}: must have a valid pack key`
|
||||||
);
|
);
|
||||||
|
const key = Bytes.toBase64(packKeyBytes);
|
||||||
|
|
||||||
drop(
|
this.stickerPacks.push({ id, key });
|
||||||
downloadStickerPack(Bytes.toHex(id), Bytes.toBase64(key), {
|
|
||||||
fromBackup: true,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async fromAdHocCall({
|
private async fromAdHocCall({
|
||||||
|
|
|
@ -875,6 +875,7 @@ type WritableInterface = {
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
createOrUpdateStickerPack: (pack: StickerPackType) => void;
|
createOrUpdateStickerPack: (pack: StickerPackType) => void;
|
||||||
|
createOrUpdateStickerPacks: (packs: ReadonlyArray<StickerPackType>) => void;
|
||||||
updateStickerPackStatus: (
|
updateStickerPackStatus: (
|
||||||
id: string,
|
id: string,
|
||||||
status: StickerPackStatusType,
|
status: StickerPackStatusType,
|
||||||
|
@ -895,6 +896,9 @@ type WritableInterface = {
|
||||||
) => ReadonlyArray<string> | undefined;
|
) => ReadonlyArray<string> | undefined;
|
||||||
deleteStickerPack: (packId: string) => Array<string>;
|
deleteStickerPack: (packId: string) => Array<string>;
|
||||||
addUninstalledStickerPack: (pack: UninstalledStickerPackType) => void;
|
addUninstalledStickerPack: (pack: UninstalledStickerPackType) => void;
|
||||||
|
addUninstalledStickerPacks: (
|
||||||
|
pack: ReadonlyArray<UninstalledStickerPackType>
|
||||||
|
) => void;
|
||||||
removeUninstalledStickerPack: (packId: string) => void;
|
removeUninstalledStickerPack: (packId: string) => void;
|
||||||
installStickerPack: (packId: string, timestamp: number) => void;
|
installStickerPack: (packId: string, timestamp: number) => void;
|
||||||
uninstallStickerPack: (packId: string, timestamp: number) => void;
|
uninstallStickerPack: (packId: string, timestamp: number) => void;
|
||||||
|
|
|
@ -486,6 +486,7 @@ export const DataWriter: ServerWritableInterface = {
|
||||||
saveBackupCdnObjectMetadata,
|
saveBackupCdnObjectMetadata,
|
||||||
|
|
||||||
createOrUpdateStickerPack,
|
createOrUpdateStickerPack,
|
||||||
|
createOrUpdateStickerPacks,
|
||||||
updateStickerPackStatus,
|
updateStickerPackStatus,
|
||||||
updateStickerPackInfo,
|
updateStickerPackInfo,
|
||||||
createOrUpdateSticker,
|
createOrUpdateSticker,
|
||||||
|
@ -495,6 +496,7 @@ export const DataWriter: ServerWritableInterface = {
|
||||||
deleteStickerPackReference,
|
deleteStickerPackReference,
|
||||||
deleteStickerPack,
|
deleteStickerPack,
|
||||||
addUninstalledStickerPack,
|
addUninstalledStickerPack,
|
||||||
|
addUninstalledStickerPacks,
|
||||||
removeUninstalledStickerPack,
|
removeUninstalledStickerPack,
|
||||||
installStickerPack,
|
installStickerPack,
|
||||||
uninstallStickerPack,
|
uninstallStickerPack,
|
||||||
|
@ -5236,6 +5238,16 @@ function createOrUpdateStickerPack(
|
||||||
`
|
`
|
||||||
).run(payload);
|
).run(payload);
|
||||||
}
|
}
|
||||||
|
function createOrUpdateStickerPacks(
|
||||||
|
db: WritableDB,
|
||||||
|
packs: ReadonlyArray<StickerPackType>
|
||||||
|
): void {
|
||||||
|
db.transaction(() => {
|
||||||
|
for (const pack of packs) {
|
||||||
|
createOrUpdateStickerPack(db, pack);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
function updateStickerPackStatus(
|
function updateStickerPackStatus(
|
||||||
db: WritableDB,
|
db: WritableDB,
|
||||||
id: string,
|
id: string,
|
||||||
|
@ -5630,6 +5642,16 @@ function addUninstalledStickerPack(
|
||||||
storageNeedsSync: pack.storageNeedsSync ? 1 : 0,
|
storageNeedsSync: pack.storageNeedsSync ? 1 : 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function addUninstalledStickerPacks(
|
||||||
|
db: WritableDB,
|
||||||
|
packs: ReadonlyArray<UninstalledStickerPackType>
|
||||||
|
): void {
|
||||||
|
return db.transaction(() => {
|
||||||
|
for (const pack of packs) {
|
||||||
|
addUninstalledStickerPack(db, pack);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
function removeUninstalledStickerPack(db: WritableDB, packId: string): void {
|
function removeUninstalledStickerPack(db: WritableDB, packId: string): void {
|
||||||
db.prepare<Query>(
|
db.prepare<Query>(
|
||||||
'DELETE FROM uninstalled_sticker_packs WHERE id IS $id'
|
'DELETE FROM uninstalled_sticker_packs WHERE id IS $id'
|
||||||
|
|
|
@ -94,18 +94,24 @@ describe('backup/bubble messages', () => {
|
||||||
timestamp: 5,
|
timestamp: 5,
|
||||||
received_at: 5,
|
received_at: 5,
|
||||||
received_at_ms: 5,
|
received_at_ms: 5,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
unidentifiedDeliveryReceived: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: 'c',
|
body: 'c',
|
||||||
timestamp: 4,
|
timestamp: 4,
|
||||||
received_at: 4,
|
received_at: 4,
|
||||||
received_at_ms: 4,
|
received_at_ms: 4,
|
||||||
|
readStatus: ReadStatus.Unread,
|
||||||
|
unidentifiedDeliveryReceived: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
body: 'b',
|
body: 'b',
|
||||||
timestamp: 3,
|
timestamp: 3,
|
||||||
received_at: 3,
|
received_at: 3,
|
||||||
received_at_ms: 3,
|
received_at_ms: 3,
|
||||||
|
readStatus: ReadStatus.Read,
|
||||||
|
unidentifiedDeliveryReceived: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,6 +14,7 @@ import {
|
||||||
} from '@signalapp/libsignal-client/dist/MessageBackup';
|
} from '@signalapp/libsignal-client/dist/MessageBackup';
|
||||||
|
|
||||||
import { FileStream } from '../../services/backups/util/FileStream';
|
import { FileStream } from '../../services/backups/util/FileStream';
|
||||||
|
import { drop } from '../../util/drop';
|
||||||
import type { App } from '../playwright';
|
import type { App } from '../playwright';
|
||||||
import { Bootstrap } from '../bootstrap';
|
import { Bootstrap } from '../bootstrap';
|
||||||
|
|
||||||
|
@ -96,11 +97,16 @@ async function runOne(filePath: string): Promise<void> {
|
||||||
await bootstrap.saveLogs(app, basename(filePath));
|
await bootstrap.saveLogs(app, basename(filePath));
|
||||||
fail(filePath, error.stack);
|
fail(filePath, error.stack);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
// No need to block on this
|
||||||
await bootstrap.teardown();
|
drop(
|
||||||
} catch (error) {
|
(async () => {
|
||||||
console.error(`Failed to teardown ${basename(filePath)}`, error);
|
try {
|
||||||
}
|
await bootstrap.teardown();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to teardown ${basename(filePath)}`, error);
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import type {
|
||||||
StickerType as StickerFromDBType,
|
StickerType as StickerFromDBType,
|
||||||
StickerPackType,
|
StickerPackType,
|
||||||
StickerPackStatusType,
|
StickerPackStatusType,
|
||||||
|
UninstalledStickerPackType,
|
||||||
} from '../sql/Interface';
|
} from '../sql/Interface';
|
||||||
import { DataReader, DataWriter } from '../sql/Client';
|
import { DataReader, DataWriter } from '../sql/Client';
|
||||||
import { SignalService as Proto } from '../protobuf';
|
import { SignalService as Proto } from '../protobuf';
|
||||||
|
@ -62,6 +63,11 @@ export type DownloadMap = Record<
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type StickerPackPointerType = Readonly<{
|
||||||
|
id: string;
|
||||||
|
key: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
export const STICKERPACK_ID_BYTE_LEN = 16;
|
export const STICKERPACK_ID_BYTE_LEN = 16;
|
||||||
export const STICKERPACK_KEY_BYTE_LEN = 32;
|
export const STICKERPACK_KEY_BYTE_LEN = 32;
|
||||||
|
|
||||||
|
@ -135,9 +141,65 @@ export async function load(): Promise<void> {
|
||||||
packsToDownload = capturePacksToDownload(packs);
|
packsToDownload = capturePacksToDownload(packs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function createPacksFromBackup(
|
||||||
|
packs: ReadonlyArray<StickerPackPointerType>
|
||||||
|
): Promise<void> {
|
||||||
|
const known = new Set(packs.map(({ id }) => id));
|
||||||
|
const pairs = packs.slice();
|
||||||
|
const uninstalled = new Array<UninstalledStickerPackType>();
|
||||||
|
|
||||||
|
for (const [id, { key }] of Object.entries(BLESSED_PACKS)) {
|
||||||
|
if (known.has(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blessed packs that are not in the backup were uninstalled
|
||||||
|
pairs.push({ id, key });
|
||||||
|
uninstalled.push({
|
||||||
|
id,
|
||||||
|
key: undefined,
|
||||||
|
uninstalledAt: Date.now(),
|
||||||
|
storageNeedsSync: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const packsToStore = pairs.map(
|
||||||
|
({ id, key }): StickerPackType => ({
|
||||||
|
...STICKER_PACK_DEFAULTS,
|
||||||
|
|
||||||
|
id,
|
||||||
|
key,
|
||||||
|
status: 'known' as const,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await DataWriter.createOrUpdateStickerPacks(packsToStore);
|
||||||
|
await DataWriter.addUninstalledStickerPacks(uninstalled);
|
||||||
|
|
||||||
|
packsToDownload = capturePacksToDownload(makeLookup(packsToStore, 'id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getStickerPacksForBackup(): Promise<
|
||||||
|
Array<StickerPackPointerType>
|
||||||
|
> {
|
||||||
|
const result = new Array<StickerPackPointerType>();
|
||||||
|
const stickerPacks = await DataReader.getAllStickerPacks();
|
||||||
|
const uninstalled = new Set(
|
||||||
|
(await DataReader.getUninstalledStickerPacks()).map(({ id }) => id)
|
||||||
|
);
|
||||||
|
for (const { id, key } of stickerPacks) {
|
||||||
|
if (uninstalled.has(id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({ id, key });
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
export function getDataFromLink(
|
export function getDataFromLink(
|
||||||
link: string
|
link: string
|
||||||
): undefined | { id: string; key: string } {
|
): undefined | StickerPackPointerType {
|
||||||
const url = maybeParseUrl(link);
|
const url = maybeParseUrl(link);
|
||||||
if (!url) {
|
if (!url) {
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
|
@ -124,6 +124,9 @@ export async function handleEditMessage(
|
||||||
timestamp: mainMessage.timestamp,
|
timestamp: mainMessage.timestamp,
|
||||||
received_at: mainMessage.received_at,
|
received_at: mainMessage.received_at,
|
||||||
received_at_ms: mainMessage.received_at_ms,
|
received_at_ms: mainMessage.received_at_ms,
|
||||||
|
serverTimestamp: mainMessage.serverTimestamp,
|
||||||
|
readStatus: mainMessage.readStatus,
|
||||||
|
unidentifiedDeliveryReceived: mainMessage.unidentifiedDeliveryReceived,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -258,6 +261,10 @@ export async function handleEditMessage(
|
||||||
timestamp: upgradedEditedMessageData.timestamp,
|
timestamp: upgradedEditedMessageData.timestamp,
|
||||||
received_at: upgradedEditedMessageData.received_at,
|
received_at: upgradedEditedMessageData.received_at,
|
||||||
received_at_ms: upgradedEditedMessageData.received_at_ms,
|
received_at_ms: upgradedEditedMessageData.received_at_ms,
|
||||||
|
serverTimestamp: upgradedEditedMessageData.serverTimestamp,
|
||||||
|
readStatus: upgradedEditedMessageData.readStatus,
|
||||||
|
unidentifiedDeliveryReceived:
|
||||||
|
upgradedEditedMessageData.unidentifiedDeliveryReceived,
|
||||||
quote: nextEditedMessageQuote,
|
quote: nextEditedMessageQuote,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue