Resolve sticker pack references after import
This commit is contained in:
parent
fbdf589f13
commit
9fab74e867
7 changed files with 216 additions and 40 deletions
|
@ -4155,7 +4155,12 @@ export class ConversationModel extends window.Backbone
|
||||||
await this.#beforeAddSingleMessage(model.attributes);
|
await this.#beforeAddSingleMessage(model.attributes);
|
||||||
|
|
||||||
if (sticker) {
|
if (sticker) {
|
||||||
await addStickerPackReference(model.id, sticker.packId);
|
await addStickerPackReference({
|
||||||
|
messageId: model.id,
|
||||||
|
packId: sticker.packId,
|
||||||
|
stickerId: sticker.stickerId,
|
||||||
|
isUnresolved: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.beforeMessageSend({
|
this.beforeMessageSend({
|
||||||
|
|
|
@ -365,6 +365,13 @@ export type StickerPackType = InstalledStickerPackType &
|
||||||
title: string;
|
title: string;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
export type StickerPackRefType = Readonly<{
|
||||||
|
packId: string;
|
||||||
|
messageId: string;
|
||||||
|
stickerId: number;
|
||||||
|
isUnresolved: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type UnprocessedType = {
|
export type UnprocessedType = {
|
||||||
id: string;
|
id: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
|
@ -940,12 +947,14 @@ type WritableInterface = {
|
||||||
stickerId: number,
|
stickerId: number,
|
||||||
lastUsed: number
|
lastUsed: number
|
||||||
) => void;
|
) => void;
|
||||||
addStickerPackReference: (messageId: string, packId: string) => void;
|
addStickerPackReference: (ref: StickerPackRefType) => void;
|
||||||
deleteStickerPackReference: (
|
deleteStickerPackReference: (
|
||||||
messageId: string,
|
ref: Pick<StickerPackRefType, 'messageId' | 'packId'>
|
||||||
packId: string
|
|
||||||
) => ReadonlyArray<string> | undefined;
|
) => ReadonlyArray<string> | undefined;
|
||||||
deleteStickerPack: (packId: string) => Array<string>;
|
deleteStickerPack: (packId: string) => Array<string>;
|
||||||
|
getUnresolvedStickerPackReferences: (
|
||||||
|
packId: string
|
||||||
|
) => Array<StickerPackRefType>;
|
||||||
addUninstalledStickerPack: (pack: UninstalledStickerPackType) => void;
|
addUninstalledStickerPack: (pack: UninstalledStickerPackType) => void;
|
||||||
addUninstalledStickerPacks: (
|
addUninstalledStickerPacks: (
|
||||||
pack: ReadonlyArray<UninstalledStickerPackType>
|
pack: ReadonlyArray<UninstalledStickerPackType>
|
||||||
|
|
|
@ -160,6 +160,7 @@ import type {
|
||||||
SignedPreKeyIdType,
|
SignedPreKeyIdType,
|
||||||
StickerPackInfoType,
|
StickerPackInfoType,
|
||||||
StickerPackStatusType,
|
StickerPackStatusType,
|
||||||
|
StickerPackRefType,
|
||||||
StickerPackType,
|
StickerPackType,
|
||||||
StickerType,
|
StickerType,
|
||||||
StoredAllItemsType,
|
StoredAllItemsType,
|
||||||
|
@ -503,6 +504,7 @@ export const DataWriter: ServerWritableInterface = {
|
||||||
addStickerPackReference,
|
addStickerPackReference,
|
||||||
deleteStickerPackReference,
|
deleteStickerPackReference,
|
||||||
deleteStickerPack,
|
deleteStickerPack,
|
||||||
|
getUnresolvedStickerPackReferences,
|
||||||
addUninstalledStickerPack,
|
addUninstalledStickerPack,
|
||||||
addUninstalledStickerPacks,
|
addUninstalledStickerPacks,
|
||||||
removeUninstalledStickerPack,
|
removeUninstalledStickerPack,
|
||||||
|
@ -5570,8 +5572,7 @@ function updateStickerLastUsed(
|
||||||
}
|
}
|
||||||
function addStickerPackReference(
|
function addStickerPackReference(
|
||||||
db: WritableDB,
|
db: WritableDB,
|
||||||
messageId: string,
|
{ messageId, packId, stickerId, isUnresolved }: StickerPackRefType
|
||||||
packId: string
|
|
||||||
): void {
|
): void {
|
||||||
if (!messageId) {
|
if (!messageId) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -5584,37 +5585,32 @@ function addStickerPackReference(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.prepare<Query>(
|
prepare(
|
||||||
|
db,
|
||||||
`
|
`
|
||||||
INSERT OR REPLACE INTO sticker_references (
|
INSERT OR REPLACE INTO sticker_references (
|
||||||
messageId,
|
messageId,
|
||||||
packId
|
packId,
|
||||||
|
stickerId,
|
||||||
|
isUnresolved
|
||||||
) values (
|
) values (
|
||||||
$messageId,
|
$messageId,
|
||||||
$packId
|
$packId,
|
||||||
|
$stickerId,
|
||||||
|
$isUnresolved
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
).run({
|
).run({
|
||||||
messageId,
|
messageId,
|
||||||
packId,
|
packId,
|
||||||
|
stickerId,
|
||||||
|
isUnresolved: isUnresolved ? 1 : 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function deleteStickerPackReference(
|
function deleteStickerPackReference(
|
||||||
db: WritableDB,
|
db: WritableDB,
|
||||||
messageId: string,
|
{ messageId, packId }: Pick<StickerPackRefType, 'messageId' | 'packId'>
|
||||||
packId: string
|
|
||||||
): ReadonlyArray<string> | undefined {
|
): ReadonlyArray<string> | undefined {
|
||||||
if (!messageId) {
|
|
||||||
throw new Error(
|
|
||||||
'addStickerPackReference: Provided data did not have a truthy messageId'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!packId) {
|
|
||||||
throw new Error(
|
|
||||||
'addStickerPackReference: Provided data did not have a truthy packId'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return db.transaction(() => {
|
return db.transaction(() => {
|
||||||
// We use an immediate transaction here to immediately acquire an exclusive lock,
|
// We use an immediate transaction here to immediately acquire an exclusive lock,
|
||||||
// which would normally only happen when we did our first write.
|
// which would normally only happen when we did our first write.
|
||||||
|
@ -5690,6 +5686,27 @@ function deleteStickerPackReference(
|
||||||
return (stickerPathRows || []).map(row => row.path);
|
return (stickerPathRows || []).map(row => row.path);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
function getUnresolvedStickerPackReferences(
|
||||||
|
db: WritableDB,
|
||||||
|
packId: string
|
||||||
|
): Array<StickerPackRefType> {
|
||||||
|
return db.transaction(() => {
|
||||||
|
const [query, params] = sql`
|
||||||
|
UPDATE sticker_references
|
||||||
|
SET isUnresolved = 0
|
||||||
|
WHERE packId IS ${packId} AND isUnresolved IS 1
|
||||||
|
RETURNING messageId, stickerId;
|
||||||
|
`;
|
||||||
|
const rows = db.prepare(query).all(params);
|
||||||
|
|
||||||
|
return rows.map(({ messageId, stickerId }) => ({
|
||||||
|
messageId,
|
||||||
|
packId,
|
||||||
|
stickerId,
|
||||||
|
isUnresolved: true,
|
||||||
|
}));
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
function deleteStickerPack(db: WritableDB, packId: string): Array<string> {
|
function deleteStickerPack(db: WritableDB, packId: string): Array<string> {
|
||||||
if (!packId) {
|
if (!packId) {
|
||||||
|
|
35
ts/sql/migrations/1300-sticker-pack-refs.ts
Normal file
35
ts/sql/migrations/1300-sticker-pack-refs.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2025 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
import type { LoggerType } from '../../types/Logging';
|
||||||
|
import { sql } from '../util';
|
||||||
|
import type { WritableDB } from '../Interface';
|
||||||
|
|
||||||
|
export const version = 1300;
|
||||||
|
|
||||||
|
export function updateToSchemaVersion1300(
|
||||||
|
currentVersion: number,
|
||||||
|
db: WritableDB,
|
||||||
|
logger: LoggerType
|
||||||
|
): void {
|
||||||
|
if (currentVersion >= 1300) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.transaction(() => {
|
||||||
|
const [query] = sql`
|
||||||
|
ALTER TABLE sticker_references
|
||||||
|
ADD COLUMN stickerId INTEGER NOT NULL DEFAULT -1;
|
||||||
|
ALTER TABLE sticker_references
|
||||||
|
ADD COLUMN isUnresolved INTEGER NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
CREATE INDEX unresolved_sticker_refs
|
||||||
|
ON sticker_references (packId, stickerId)
|
||||||
|
WHERE isUnresolved IS 1;
|
||||||
|
`;
|
||||||
|
db.exec(query);
|
||||||
|
|
||||||
|
db.pragma('user_version = 1300');
|
||||||
|
})();
|
||||||
|
|
||||||
|
logger.info('updateToSchemaVersion1300: success!');
|
||||||
|
}
|
|
@ -105,10 +105,11 @@ import { updateToSchemaVersion1250 } from './1250-defunct-call-links-storage';
|
||||||
import { updateToSchemaVersion1260 } from './1260-sync-tasks-rowid';
|
import { updateToSchemaVersion1260 } from './1260-sync-tasks-rowid';
|
||||||
import { updateToSchemaVersion1270 } from './1270-normalize-messages';
|
import { updateToSchemaVersion1270 } from './1270-normalize-messages';
|
||||||
import { updateToSchemaVersion1280 } from './1280-blob-unprocessed';
|
import { updateToSchemaVersion1280 } from './1280-blob-unprocessed';
|
||||||
|
import { updateToSchemaVersion1290 } from './1290-int-unprocessed-source-device';
|
||||||
import {
|
import {
|
||||||
updateToSchemaVersion1290,
|
updateToSchemaVersion1300,
|
||||||
version as MAX_VERSION,
|
version as MAX_VERSION,
|
||||||
} from './1290-int-unprocessed-source-device';
|
} from './1300-sticker-pack-refs';
|
||||||
import { DataWriter } from '../Server';
|
import { DataWriter } from '../Server';
|
||||||
|
|
||||||
function updateToSchemaVersion1(
|
function updateToSchemaVersion1(
|
||||||
|
@ -2084,6 +2085,8 @@ export const SCHEMA_VERSIONS = [
|
||||||
updateToSchemaVersion1270,
|
updateToSchemaVersion1270,
|
||||||
updateToSchemaVersion1280,
|
updateToSchemaVersion1280,
|
||||||
updateToSchemaVersion1290,
|
updateToSchemaVersion1290,
|
||||||
|
|
||||||
|
updateToSchemaVersion1300,
|
||||||
];
|
];
|
||||||
|
|
||||||
export class DBVersionFromFutureError extends Error {
|
export class DBVersionFromFutureError extends Error {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2019 Signal Messenger, LLC
|
// Copyright 2019 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import { isNumber, reject, groupBy, values } from 'lodash';
|
import { isNumber, reject, groupBy, values, chunk } from 'lodash';
|
||||||
import pMap from 'p-map';
|
import pMap from 'p-map';
|
||||||
import Queue from 'p-queue';
|
import Queue from 'p-queue';
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { strictAssert } from '../util/assert';
|
||||||
import { dropNull } from '../util/dropNull';
|
import { dropNull } from '../util/dropNull';
|
||||||
import { makeLookup } from '../util/makeLookup';
|
import { makeLookup } from '../util/makeLookup';
|
||||||
import { maybeParseUrl } from '../util/url';
|
import { maybeParseUrl } from '../util/url';
|
||||||
|
import { getMessagesById } from '../messages/getMessagesById';
|
||||||
import * as Bytes from '../Bytes';
|
import * as Bytes from '../Bytes';
|
||||||
import * as Errors from './errors';
|
import * as Errors from './errors';
|
||||||
import { deriveStickerPackKey, decryptAttachmentV1 } from '../Crypto';
|
import { deriveStickerPackKey, decryptAttachmentV1 } from '../Crypto';
|
||||||
|
@ -71,6 +72,10 @@ export type StickerPackPointerType = Readonly<{
|
||||||
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;
|
||||||
|
|
||||||
|
// Number of messages loaded and saved at the same time when resolving sticker
|
||||||
|
// pack references.
|
||||||
|
const RESOLVE_REFERENCES_BATCH_SIZE = 1000;
|
||||||
|
|
||||||
const BLESSED_PACKS: Record<string, BlessedType> = {
|
const BLESSED_PACKS: Record<string, BlessedType> = {
|
||||||
'9acc9e8aba563d26a4994e69263e3b25': {
|
'9acc9e8aba563d26a4994e69263e3b25': {
|
||||||
key: 'Wm3/OUjCjvubeq+T7MN1xp/DFueAd+0mhnoU0QoPahI=',
|
key: 'Wm3/OUjCjvubeq+T7MN1xp/DFueAd+0mhnoU0QoPahI=',
|
||||||
|
@ -428,7 +433,11 @@ async function downloadSticker(
|
||||||
export async function savePackMetadata(
|
export async function savePackMetadata(
|
||||||
packId: string,
|
packId: string,
|
||||||
packKey: string,
|
packKey: string,
|
||||||
{ messageId }: { messageId?: string } = {}
|
{
|
||||||
|
messageId,
|
||||||
|
stickerId,
|
||||||
|
isUnresolved,
|
||||||
|
}: { messageId: string; stickerId: number; isUnresolved: boolean }
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const existing = getStickerPack(packId);
|
const existing = getStickerPack(packId);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
@ -447,7 +456,12 @@ export async function savePackMetadata(
|
||||||
|
|
||||||
await DataWriter.createOrUpdateStickerPack(pack);
|
await DataWriter.createOrUpdateStickerPack(pack);
|
||||||
if (messageId) {
|
if (messageId) {
|
||||||
await DataWriter.addStickerPackReference(messageId, packId);
|
await DataWriter.addStickerPackReference({
|
||||||
|
messageId,
|
||||||
|
packId,
|
||||||
|
stickerId,
|
||||||
|
isUnresolved,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,7 +634,6 @@ export async function downloadEphemeralPack(
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DownloadStickerPackOptions = Readonly<{
|
export type DownloadStickerPackOptions = Readonly<{
|
||||||
messageId?: string;
|
|
||||||
fromSync?: boolean;
|
fromSync?: boolean;
|
||||||
fromStorageService?: boolean;
|
fromStorageService?: boolean;
|
||||||
fromBackup?: boolean;
|
fromBackup?: boolean;
|
||||||
|
@ -651,7 +664,6 @@ async function doDownloadStickerPack(
|
||||||
packKey: string,
|
packKey: string,
|
||||||
{
|
{
|
||||||
finalStatus = 'downloaded',
|
finalStatus = 'downloaded',
|
||||||
messageId,
|
|
||||||
fromSync = false,
|
fromSync = false,
|
||||||
fromStorageService = false,
|
fromStorageService = false,
|
||||||
fromBackup = false,
|
fromBackup = false,
|
||||||
|
@ -782,10 +794,6 @@ async function doDownloadStickerPack(
|
||||||
};
|
};
|
||||||
await DataWriter.createOrUpdateStickerPack(pack);
|
await DataWriter.createOrUpdateStickerPack(pack);
|
||||||
stickerPackAdded(pack);
|
stickerPackAdded(pack);
|
||||||
|
|
||||||
if (messageId) {
|
|
||||||
await DataWriter.addStickerPackReference(messageId, packId);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(
|
log.error(
|
||||||
`Error downloading manifest for sticker pack ${redactPackId(packId)}:`,
|
`Error downloading manifest for sticker pack ${redactPackId(packId)}:`,
|
||||||
|
@ -854,10 +862,8 @@ async function doDownloadStickerPack(
|
||||||
// don't overwrite that status.
|
// don't overwrite that status.
|
||||||
const existingStatus = getStickerPackStatus(packId);
|
const existingStatus = getStickerPackStatus(packId);
|
||||||
if (existingStatus === 'installed') {
|
if (existingStatus === 'installed') {
|
||||||
return;
|
// No-op
|
||||||
}
|
} else if (finalStatus === 'installed') {
|
||||||
|
|
||||||
if (finalStatus === 'installed') {
|
|
||||||
await installStickerPack(packId, packKey, {
|
await installStickerPack(packId, packKey, {
|
||||||
fromSync,
|
fromSync,
|
||||||
fromStorageService,
|
fromStorageService,
|
||||||
|
@ -870,6 +876,8 @@ async function doDownloadStickerPack(
|
||||||
status: finalStatus,
|
status: finalStatus,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop(safeResolveReferences(packId));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(
|
log.error(
|
||||||
`Error downloading stickers for sticker pack ${redactPackId(packId)}:`,
|
`Error downloading stickers for sticker pack ${redactPackId(packId)}:`,
|
||||||
|
@ -891,6 +899,96 @@ async function doDownloadStickerPack(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function safeResolveReferences(packId: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await resolveReferences(packId);
|
||||||
|
} catch (error) {
|
||||||
|
const logId = `Stickers.resolveReferences(${redactPackId(packId)})`;
|
||||||
|
log.error(`${logId}: failed`, Errors.toLogFormat(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resolveReferences(packId: string): Promise<void> {
|
||||||
|
const refs = await DataWriter.getUnresolvedStickerPackReferences(packId);
|
||||||
|
if (refs.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logId = `Stickers.resolveReferences(${redactPackId(packId)})`;
|
||||||
|
log.info(`${logId}: resolving ${refs.length}`);
|
||||||
|
|
||||||
|
const stickerIdToMessageIds = new Map<number, Array<string>>();
|
||||||
|
for (const { stickerId, messageId } of refs) {
|
||||||
|
let list = stickerIdToMessageIds.get(stickerId);
|
||||||
|
if (list == null) {
|
||||||
|
list = [];
|
||||||
|
stickerIdToMessageIds.set(stickerId, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(messageId);
|
||||||
|
}
|
||||||
|
|
||||||
|
await pMap(
|
||||||
|
Array.from(stickerIdToMessageIds.entries()),
|
||||||
|
([stickerId, messageIds]) =>
|
||||||
|
pMap(
|
||||||
|
chunk(messageIds, RESOLVE_REFERENCES_BATCH_SIZE),
|
||||||
|
async batch => {
|
||||||
|
let attachments: Array<AttachmentType>;
|
||||||
|
try {
|
||||||
|
attachments = await pMap(
|
||||||
|
messageIds,
|
||||||
|
() => copyStickerToAttachments(packId, stickerId),
|
||||||
|
{ concurrency: 3 }
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
log.error(
|
||||||
|
`${logId}: failed to copy sticker ${stickerId}`,
|
||||||
|
Errors.toLogFormat(error)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const messages = await getMessagesById(batch);
|
||||||
|
|
||||||
|
const saves = new Array<Promise<unknown>>();
|
||||||
|
for (const [index, message] of messages.entries()) {
|
||||||
|
const data = attachments[index];
|
||||||
|
strictAssert(data != null, 'Missing copied data');
|
||||||
|
|
||||||
|
const { sticker, sent_at: sentAt } = message.attributes;
|
||||||
|
if (!sticker) {
|
||||||
|
log.info(`${logId}: ${sentAt} has no sticker`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sticker?.data?.path) {
|
||||||
|
log.info(`${logId}: ${sentAt} already downloaded`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sticker.packId !== packId || sticker.stickerId !== stickerId) {
|
||||||
|
log.info(`${logId}: ${sentAt} has different sticker`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.set({
|
||||||
|
sticker: {
|
||||||
|
...sticker,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
saves.push(window.MessageCache.saveMessage(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(saves);
|
||||||
|
},
|
||||||
|
{ concurrency: 1 }
|
||||||
|
),
|
||||||
|
{ concurrency: 3 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getStickerPack(packId: string): StickerPackType | undefined {
|
export function getStickerPack(packId: string): StickerPackType | undefined {
|
||||||
const state = window.reduxStore.getState();
|
const state = window.reduxStore.getState();
|
||||||
const { stickers } = state;
|
const { stickers } = state;
|
||||||
|
@ -988,7 +1086,10 @@ export async function deletePackReference(
|
||||||
|
|
||||||
// This call uses locking to prevent race conditions with other reference removals,
|
// This call uses locking to prevent race conditions with other reference removals,
|
||||||
// or an incoming message creating a new message->pack reference
|
// or an incoming message creating a new message->pack reference
|
||||||
const paths = await DataWriter.deleteStickerPackReference(messageId, packId);
|
const paths = await DataWriter.deleteStickerPackReference({
|
||||||
|
messageId,
|
||||||
|
packId,
|
||||||
|
});
|
||||||
|
|
||||||
// If we don't get a list of paths back, then the sticker pack was not deleted
|
// If we don't get a list of paths back, then the sticker pack was not deleted
|
||||||
if (!paths) {
|
if (!paths) {
|
||||||
|
|
|
@ -314,11 +314,17 @@ export async function queueAttachmentDownloads(
|
||||||
log.error(`${idLog}: Sticker data was missing`);
|
log.error(`${idLog}: Sticker data was missing`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const stickerRef = {
|
||||||
|
messageId,
|
||||||
|
packId,
|
||||||
|
stickerId,
|
||||||
|
isUnresolved: sticker.data?.error === true,
|
||||||
|
};
|
||||||
if (!status) {
|
if (!status) {
|
||||||
// Save the packId/packKey for future download/install
|
// Save the packId/packKey for future download/install
|
||||||
void savePackMetadata(packId, packKey, { messageId });
|
void savePackMetadata(packId, packKey, stickerRef);
|
||||||
} else {
|
} else {
|
||||||
await DataWriter.addStickerPackReference(messageId, packId);
|
await DataWriter.addStickerPackReference(stickerRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue