diff --git a/ts/background.ts b/ts/background.ts index de1fbf0977..0190a0ff20 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -2343,17 +2343,17 @@ export async function startApp(): Promise { if (status === 'installed' && isRemove) { window.reduxActions.stickers.uninstallStickerPack(id, key, { - fromSync: true, + actionSource: 'syncMessage', }); } else if (isInstall) { if (status === 'downloaded') { window.reduxActions.stickers.installStickerPack(id, key, { - fromSync: true, + actionSource: 'syncMessage', }); } else { void Stickers.downloadStickerPack(id, key, { finalStatus: 'installed', - fromSync: true, + actionSource: 'syncMessage', }); } } diff --git a/ts/components/stickers/StickerManager.tsx b/ts/components/stickers/StickerManager.tsx index da63e91ace..ebc1ecc09e 100644 --- a/ts/components/stickers/StickerManager.tsx +++ b/ts/components/stickers/StickerManager.tsx @@ -11,13 +11,25 @@ import { Tabs } from '../Tabs'; export type OwnProps = { readonly blessedPacks: ReadonlyArray; readonly closeStickerPackPreview: () => unknown; - readonly downloadStickerPack: (packId: string, packKey: string) => unknown; + readonly downloadStickerPack: ( + packId: string, + packKey: string, + options: { actionSource: 'ui' } + ) => unknown; readonly i18n: LocalizerType; - readonly installStickerPack: (packId: string, packKey: string) => unknown; + readonly installStickerPack: ( + packId: string, + packKey: string, + options: { actionSource: 'ui' } + ) => unknown; readonly installedPacks: ReadonlyArray; readonly knownPacks?: ReadonlyArray; readonly receivedPacks: ReadonlyArray; - readonly uninstallStickerPack: (packId: string, packKey: string) => unknown; + readonly uninstallStickerPack: ( + packId: string, + packKey: string, + options: { actionSource: 'ui' } + ) => unknown; }; export type Props = OwnProps; @@ -47,7 +59,7 @@ export const StickerManager = React.memo(function StickerManagerInner({ return; } knownPacks.forEach(pack => { - downloadStickerPack(pack.id, pack.key); + downloadStickerPack(pack.id, pack.key, { actionSource: 'ui' }); }); // When this component is created, it's initially not part of the DOM, and then it's diff --git a/ts/components/stickers/StickerManagerPackRow.tsx b/ts/components/stickers/StickerManagerPackRow.tsx index 8b4b312cbb..585c398b01 100644 --- a/ts/components/stickers/StickerManagerPackRow.tsx +++ b/ts/components/stickers/StickerManagerPackRow.tsx @@ -12,8 +12,16 @@ export type OwnProps = { readonly i18n: LocalizerType; readonly pack: StickerPackType; readonly onClickPreview?: (sticker: StickerPackType) => unknown; - readonly installStickerPack?: (packId: string, packKey: string) => unknown; - readonly uninstallStickerPack?: (packId: string, packKey: string) => unknown; + readonly installStickerPack?: ( + packId: string, + packKey: string, + options: { actionSource: 'ui' } + ) => unknown; + readonly uninstallStickerPack?: ( + packId: string, + packKey: string, + options: { actionSource: 'ui' } + ) => unknown; }; export type Props = OwnProps; @@ -37,7 +45,7 @@ export const StickerManagerPackRow = React.memo( (e: React.MouseEvent) => { e.stopPropagation(); if (installStickerPack) { - installStickerPack(id, key); + installStickerPack(id, key, { actionSource: 'ui' }); } }, [id, installStickerPack, key] @@ -47,7 +55,7 @@ export const StickerManagerPackRow = React.memo( (e: React.MouseEvent) => { e.stopPropagation(); if (isBlessed && uninstallStickerPack) { - uninstallStickerPack(id, key); + uninstallStickerPack(id, key, { actionSource: 'ui' }); } else { setUninstalling(true); } @@ -58,7 +66,7 @@ export const StickerManagerPackRow = React.memo( const handleConfirmUninstall = React.useCallback(() => { clearUninstalling(); if (uninstallStickerPack) { - uninstallStickerPack(id, key); + uninstallStickerPack(id, key, { actionSource: 'ui' }); } }, [id, key, clearUninstalling, uninstallStickerPack]); diff --git a/ts/components/stickers/StickerPreviewModal.tsx b/ts/components/stickers/StickerPreviewModal.tsx index e2baa89805..e7242ee070 100644 --- a/ts/components/stickers/StickerPreviewModal.tsx +++ b/ts/components/stickers/StickerPreviewModal.tsx @@ -19,10 +19,18 @@ export type OwnProps = { readonly downloadStickerPack: ( packId: string, packKey: string, - options?: { finalStatus?: 'installed' | 'downloaded' } + options: { finalStatus?: 'installed' | 'downloaded'; actionSource: 'ui' } + ) => unknown; + readonly installStickerPack: ( + packId: string, + packKey: string, + options: { actionSource: 'ui' } + ) => unknown; + readonly uninstallStickerPack: ( + packId: string, + packKey: string, + options: { actionSource: 'ui' } ) => unknown; - readonly installStickerPack: (packId: string, packKey: string) => unknown; - readonly uninstallStickerPack: (packId: string, packKey: string) => unknown; readonly pack?: StickerPackType; readonly i18n: LocalizerType; }; @@ -90,7 +98,7 @@ export const StickerPreviewModal = React.memo( React.useEffect(() => { if (pack && pack.status === 'known') { - downloadStickerPack(pack.id, pack.key); + downloadStickerPack(pack.id, pack.key, { actionSource: 'ui' }); } if ( pack && @@ -99,6 +107,7 @@ export const StickerPreviewModal = React.memo( pack.attemptedStatus === 'installed') ) { downloadStickerPack(pack.id, pack.key, { + actionSource: 'ui', finalStatus: pack.attemptedStatus, }); } @@ -130,10 +139,13 @@ export const StickerPreviewModal = React.memo( if (isInstalled) { setConfirmingUninstall(true); } else if (pack.status === 'ephemeral') { - downloadStickerPack(pack.id, pack.key, { finalStatus: 'installed' }); + downloadStickerPack(pack.id, pack.key, { + finalStatus: 'installed', + actionSource: 'ui', + }); handleClose(); } else { - installStickerPack(pack.id, pack.key); + installStickerPack(pack.id, pack.key, { actionSource: 'ui' }); handleClose(); } }, [ @@ -149,7 +161,7 @@ export const StickerPreviewModal = React.memo( if (!pack) { return; } - uninstallStickerPack(pack.id, pack.key); + uninstallStickerPack(pack.id, pack.key, { actionSource: 'ui' }); setConfirmingUninstall(false); // closeStickerPackPreview is called by 's onClose }, [uninstallStickerPack, setConfirmingUninstall, pack]); diff --git a/ts/services/storage.ts b/ts/services/storage.ts index f503d831d8..ac65fb8545 100644 --- a/ts/services/storage.ts +++ b/ts/services/storage.ts @@ -409,13 +409,9 @@ async function generateManifest( } } - log.info( - `storageService.upload(${version}): ` + - `adding uninstalled stickerPacks=${uninstalledStickerPacks.length}` - ); - const uninstalledStickerPackIds = new Set(); + let newlyUninstalledPacks = 0; uninstalledStickerPacks.forEach(stickerPack => { const storageRecord = new Proto.StorageRecord(); storageRecord.stickerPack = toStickerPackRecord(stickerPack); @@ -431,22 +427,19 @@ async function generateManifest( }); if (isNewItem) { - postUploadUpdateFunctions.push(() => { - void DataWriter.addUninstalledStickerPack({ + newlyUninstalledPacks += 1; + postUploadUpdateFunctions.push(() => + DataWriter.addUninstalledStickerPack({ ...stickerPack, storageID, storageVersion: version, storageNeedsSync: false, - }); - }); + }) + ); } }); - log.info( - `storageService.upload(${version}): ` + - `adding installed stickerPacks=${installedStickerPacks.length}` - ); - + let newlyInstalledPacks = 0; installedStickerPacks.forEach(stickerPack => { if (uninstalledStickerPackIds.has(stickerPack.id)) { log.error( @@ -456,7 +449,7 @@ async function generateManifest( window.reduxActions.stickers.uninstallStickerPack( stickerPack.id, stickerPack.key, - { fromSync: true } + { actionSource: 'storageService' } ); return; } @@ -473,17 +466,27 @@ async function generateManifest( }); if (isNewItem) { - postUploadUpdateFunctions.push(() => { - void DataWriter.createOrUpdateStickerPack({ - ...stickerPack, + newlyInstalledPacks += 1; + postUploadUpdateFunctions.push(() => + DataWriter.updateStickerPackInfo({ + id: stickerPack.id, + key: stickerPack.key, + storageID, storageVersion: version, storageNeedsSync: false, - }); - }); + position: stickerPack.position, + }) + ); } }); + log.info( + `storageService.upload(${version}): stickerPacks ` + + `installed=${newlyInstalledPacks}/${installedStickerPacks.length} ` + + `uninstalled=${newlyUninstalledPacks}/${uninstalledStickerPacks.length}` + ); + log.info( `storageService.upload(${version}): ` + `adding callLinks=${callLinkDbRecords.length}` @@ -1498,10 +1501,15 @@ async function processManifest( `storageService.process(${version}): localKey=${missingKey} was not ` + 'in remote manifest' ); - void DataWriter.createOrUpdateStickerPack({ - ...stickerPack, + void DataWriter.updateStickerPackInfo({ + id: stickerPack.id, + key: stickerPack.key, + storageID: undefined, storageVersion: undefined, + storageUnknownFields: undefined, + storageNeedsSync: false, + uninstalledAt: stickerPack.uninstalledAt, }); }); diff --git a/ts/services/storageRecordOps.ts b/ts/services/storageRecordOps.ts index 4f24f7e654..d88b7097a0 100644 --- a/ts/services/storageRecordOps.ts +++ b/ts/services/storageRecordOps.ts @@ -1937,13 +1937,24 @@ export async function mergeStickerPackRecord( `newPosition=${stickerPack.position ?? '?'}` ); - if (localStickerPack && !wasUninstalled && isUninstalled) { - assertDev(localStickerPack.key, 'Installed sticker pack has no key'); - window.reduxActions.stickers.uninstallStickerPack( - localStickerPack.id, - localStickerPack.key, - { fromStorageService: true } - ); + if (!wasUninstalled && isUninstalled) { + if (localStickerPack != null) { + assertDev(localStickerPack.key, 'Installed sticker pack has no key'); + window.reduxActions.stickers.uninstallStickerPack( + localStickerPack.id, + localStickerPack.key, + { + actionSource: 'storageService', + uninstalledAt: stickerPack.uninstalledAt, + } + ); + } else { + strictAssert( + stickerPack.key == null && stickerPack.uninstalledAt != null, + 'Created sticker pack must be already uninstalled' + ); + await DataWriter.addUninstalledStickerPack(stickerPack); + } } else if ((!localStickerPack || wasUninstalled) && !isUninstalled) { assertDev(stickerPack.key, 'Sticker pack does not have key'); @@ -1953,13 +1964,13 @@ export async function mergeStickerPackRecord( stickerPack.id, stickerPack.key, { - fromStorageService: true, + actionSource: 'storageService', } ); } else { void Stickers.downloadStickerPack(stickerPack.id, stickerPack.key, { finalStatus: 'installed', - fromStorageService: true, + actionSource: 'storageService', }); } } diff --git a/ts/sql/Interface.ts b/ts/sql/Interface.ts index feb24105e7..03d14758e6 100644 --- a/ts/sql/Interface.ts +++ b/ts/sql/Interface.ts @@ -934,11 +934,12 @@ type WritableInterface = { createOrUpdateStickerPack: (pack: StickerPackType) => void; createOrUpdateStickerPacks: (packs: ReadonlyArray) => void; + // Returns previous sticker pack status updateStickerPackStatus: ( id: string, status: StickerPackStatusType, options?: { timestamp: number } - ) => void; + ) => StickerPackStatusType | null; updateStickerPackInfo: (info: StickerPackInfoType) => void; createOrUpdateSticker: (sticker: StickerType) => void; createOrUpdateStickers: (sticker: ReadonlyArray) => void; @@ -959,9 +960,10 @@ type WritableInterface = { addUninstalledStickerPacks: ( pack: ReadonlyArray ) => void; - removeUninstalledStickerPack: (packId: string) => void; - installStickerPack: (packId: string, timestamp: number) => void; - uninstallStickerPack: (packId: string, timestamp: number) => void; + // Returns `true` if sticker pack was previously uninstalled + installStickerPack: (packId: string, timestamp: number) => boolean; + // Returns `true` if sticker pack was not previously uninstalled + uninstallStickerPack: (packId: string, timestamp: number) => boolean; clearAllErrorStickerPackAttempts: () => void; updateEmojiUsage: (shortName: string, timeUsed?: number) => void; diff --git a/ts/sql/Server.ts b/ts/sql/Server.ts index 374728bbeb..bad24d8b71 100644 --- a/ts/sql/Server.ts +++ b/ts/sql/Server.ts @@ -507,7 +507,6 @@ export const DataWriter: ServerWritableInterface = { getUnresolvedStickerPackReferences, addUninstalledStickerPack, addUninstalledStickerPacks, - removeUninstalledStickerPack, installStickerPack, uninstallStickerPack, clearAllErrorStickerPackAttempts, @@ -5251,10 +5250,6 @@ function createOrUpdateStickerPack( status, stickerCount, title, - storageID, - storageVersion, - storageUnknownFields, - storageNeedsSync, } = pack; if (!id) { throw new Error( @@ -5262,21 +5257,6 @@ function createOrUpdateStickerPack( ); } - let { position } = pack; - - // Assign default position - if (!isNumber(position)) { - position = db - .prepare( - ` - SELECT IFNULL(MAX(position) + 1, 0) - FROM sticker_packs - ` - ) - .pluck() - .get(); - } - const row = db .prepare( ` @@ -5299,11 +5279,6 @@ function createOrUpdateStickerPack( status, stickerCount, title, - position: position ?? 0, - storageID: storageID ?? null, - storageVersion: storageVersion ?? null, - storageUnknownFields: storageUnknownFields ?? null, - storageNeedsSync: storageNeedsSync ? 1 : 0, }; if (row) { @@ -5320,12 +5295,7 @@ function createOrUpdateStickerPack( lastUsed = $lastUsed, status = $status, stickerCount = $stickerCount, - title = $title, - position = $position, - storageID = $storageID, - storageVersion = $storageVersion, - storageUnknownFields = $storageUnknownFields, - storageNeedsSync = $storageNeedsSync + title = $title WHERE id = $id; ` ).run(payload); @@ -5333,6 +5303,21 @@ function createOrUpdateStickerPack( return; } + let { position } = pack; + + // Assign default position when inserting a row + if (!isNumber(position)) { + position = db + .prepare( + ` + SELECT IFNULL(MAX(position) + 1, 0) + FROM sticker_packs + ` + ) + .pluck() + .get(); + } + db.prepare( ` INSERT INTO sticker_packs ( @@ -5348,11 +5333,7 @@ function createOrUpdateStickerPack( status, stickerCount, title, - position, - storageID, - storageVersion, - storageUnknownFields, - storageNeedsSync + position ) values ( $attemptedStatus, $author, @@ -5366,14 +5347,10 @@ function createOrUpdateStickerPack( $status, $stickerCount, $title, - $position, - $storageID, - $storageVersion, - $storageUnknownFields, - $storageNeedsSync + $position ) ` - ).run(payload); + ).run({ ...payload, position: position ?? 0 }); } function createOrUpdateStickerPacks( db: WritableDB, @@ -5390,21 +5367,27 @@ function updateStickerPackStatus( id: string, status: StickerPackStatusType, options?: { timestamp: number } -): void { +): StickerPackStatusType | null { const timestamp = options ? options.timestamp || Date.now() : Date.now(); const installedAt = status === 'installed' ? timestamp : null; - db.prepare( - ` - UPDATE sticker_packs - SET status = $status, installedAt = $installedAt - WHERE id = $id; - ` - ).run({ - id, - status, - installedAt, - }); + return db.transaction(() => { + const [select, selectParams] = sql` + SELECT status FROM sticker_packs WHERE id IS ${id}; + `; + + const oldStatus = db.prepare(select).pluck().get(selectParams); + + const [update, updateParams] = sql` + UPDATE sticker_packs + SET status = ${status}, installedAt = ${installedAt} + WHERE id IS ${id} + `; + + db.prepare(update).run(updateParams); + + return oldStatus; + })(); } function updateStickerPackInfo( db: WritableDB, @@ -5415,6 +5398,7 @@ function updateStickerPackInfo( storageUnknownFields, storageNeedsSync, uninstalledAt, + position, }: StickerPackInfoType ): void { if (uninstalledAt) { @@ -5443,7 +5427,8 @@ function updateStickerPackInfo( storageID = $storageID, storageVersion = $storageVersion, storageUnknownFields = $storageUnknownFields, - storageNeedsSync = $storageNeedsSync + storageNeedsSync = $storageNeedsSync, + position = $position WHERE id = $id; ` ).run({ @@ -5452,6 +5437,7 @@ function updateStickerPackInfo( storageVersion: storageVersion ?? null, storageUnknownFields: storageUnknownFields ?? null, storageNeedsSync: storageNeedsSync ? 1 : 0, + position: position || 0, }); } } @@ -5774,17 +5760,17 @@ function addUninstalledStickerPack( ): void { db.prepare( ` - INSERT OR REPLACE INTO uninstalled_sticker_packs - ( - id, uninstalledAt, storageID, storageVersion, storageUnknownFields, - storageNeedsSync - ) - VALUES - ( - $id, $uninstalledAt, $storageID, $storageVersion, $unknownFields, - $storageNeedsSync - ) - ` + INSERT OR REPLACE INTO uninstalled_sticker_packs + ( + id, uninstalledAt, storageID, storageVersion, storageUnknownFields, + storageNeedsSync + ) + VALUES + ( + $id, $uninstalledAt, $storageID, $storageVersion, $unknownFields, + $storageNeedsSync + ) + ` ).run({ id: pack.id, uninstalledAt: pack.uninstalledAt, @@ -5805,9 +5791,10 @@ function addUninstalledStickerPacks( })(); } function removeUninstalledStickerPack(db: WritableDB, packId: string): void { - db.prepare( - 'DELETE FROM uninstalled_sticker_packs WHERE id IS $id' - ).run({ id: packId }); + const [query, params] = sql` + DELETE FROM uninstalled_sticker_packs WHERE id IS ${packId} + `; + db.prepare(query).run(params); } function getUninstalledStickerPacks( db: ReadableDB @@ -5876,39 +5863,57 @@ function installStickerPack( db: WritableDB, packId: string, timestamp: number -): void { +): boolean { return db.transaction(() => { const status = 'installed'; - updateStickerPackStatus(db, packId, status, { timestamp }); - removeUninstalledStickerPack(db, packId); + const oldStatus = updateStickerPackStatus(db, packId, status, { + timestamp, + }); + + const wasPreviouslyUninstalled = oldStatus !== 'installed'; + + if (wasPreviouslyUninstalled) { + const [query, params] = sql` + UPDATE sticker_packs SET + storageNeedsSync = 1 + WHERE id IS ${packId}; + `; + db.prepare(query).run(params); + } + + return wasPreviouslyUninstalled; })(); } function uninstallStickerPack( db: WritableDB, packId: string, timestamp: number -): void { +): boolean { return db.transaction(() => { const status = 'downloaded'; - updateStickerPackStatus(db, packId, status); + const oldStatus = updateStickerPackStatus(db, packId, status); - db.prepare( - ` + const wasPreviouslyInstalled = oldStatus === 'installed'; + + const [query, params] = sql` UPDATE sticker_packs SET storageID = NULL, storageVersion = NULL, storageUnknownFields = NULL, storageNeedsSync = 0 - WHERE id = $packId; - ` - ).run({ packId }); + WHERE id = ${packId} + `; + + db.prepare(query).run(params); addUninstalledStickerPack(db, { id: packId, uninstalledAt: timestamp, - storageNeedsSync: true, + storageNeedsSync: wasPreviouslyInstalled, }); + + return wasPreviouslyInstalled; })(); } function getAllStickers(db: ReadableDB): Array { diff --git a/ts/state/ducks/stickers.ts b/ts/state/ducks/stickers.ts index 6e8ef2c2c8..7a0cddbc02 100644 --- a/ts/state/ducks/stickers.ts +++ b/ts/state/ducks/stickers.ts @@ -10,11 +10,12 @@ import type { StickerPackType as StickerPackDBType, } from '../../sql/Interface'; import { DataReader, DataWriter } from '../../sql/Client'; -import type { RecentStickerType } from '../../types/Stickers'; +import type { ActionSourceType, RecentStickerType } from '../../types/Stickers'; import { downloadStickerPack as externalDownloadStickerPack, maybeDeletePack, } from '../../types/Stickers'; +import { drop } from '../../util/drop'; import { storageServiceUploadJob } from '../../services/storage'; import { sendStickerPackSync } from '../../shims/textsecure'; import { trigger } from '../../shims/events'; @@ -74,7 +75,7 @@ type StickerAddedAction = ReadonlyDeep<{ type InstallStickerPackPayloadType = ReadonlyDeep<{ packId: string; - fromSync: boolean; + actionSource: ActionSourceType; status: 'installed'; installedAt: number; recentStickers: Array; @@ -93,7 +94,7 @@ type ClearInstalledStickerPackAction = ReadonlyDeep<{ type UninstallStickerPackPayloadType = ReadonlyDeep<{ packId: string; - fromSync: boolean; + actionSource: ActionSourceType; status: 'downloaded'; installedAt?: undefined; recentStickers: Array; @@ -199,12 +200,21 @@ function stickerPackAdded( function downloadStickerPack( packId: string, packKey: string, - options?: { finalStatus?: 'installed' | 'downloaded' } + { + finalStatus, + actionSource, + }: { + finalStatus?: 'installed' | 'downloaded'; + actionSource: ActionSourceType; + } ): NoopActionType { - const { finalStatus } = options || { finalStatus: undefined }; - // We're just kicking this off, since it will generate more redux events - void externalDownloadStickerPack(packId, packKey, { finalStatus }); + drop( + externalDownloadStickerPack(packId, packKey, { + finalStatus, + actionSource, + }) + ); return { type: 'NOOP', @@ -215,41 +225,33 @@ function downloadStickerPack( function installStickerPack( packId: string, packKey: string, - options: { - fromSync?: boolean; - fromStorageService?: boolean; - fromBackup?: boolean; - } = {} + { actionSource }: { actionSource: ActionSourceType } ): InstallStickerPackAction { return { type: 'stickers/INSTALL_STICKER_PACK', - payload: doInstallStickerPack(packId, packKey, options), + payload: doInstallStickerPack(packId, packKey, { actionSource }), }; } async function doInstallStickerPack( packId: string, packKey: string, - options: { - fromSync?: boolean; - fromStorageService?: boolean; - fromBackup?: boolean; - } = {} + { actionSource }: { actionSource: ActionSourceType } ): Promise { - const { - fromSync = false, - fromStorageService = false, - fromBackup = false, - } = options; - const timestamp = Date.now(); - await DataWriter.installStickerPack(packId, timestamp); + const changed = await DataWriter.installStickerPack(packId, timestamp); - if (!fromSync && !fromStorageService && !fromBackup) { + if (actionSource === 'ui') { // Kick this off, but don't wait for it - void sendStickerPackSync(packId, packKey, true); + drop(sendStickerPackSync(packId, packKey, true)); } - if (!fromStorageService && !fromBackup) { + if ( + // Don't cause storageService loop + actionSource !== 'storageService' && + // Stickers downloaded on startup should already be synced + actionSource !== 'startup' && + changed + ) { storageServiceUploadJob({ reason: 'doInstallServicePack' }); } @@ -257,7 +259,7 @@ async function doInstallStickerPack( return { packId, - fromSync, + actionSource, status: 'installed', installedAt: timestamp, recentStickers: recentStickers.map(item => ({ @@ -269,32 +271,44 @@ async function doInstallStickerPack( function uninstallStickerPack( packId: string, packKey: string, - options: { fromSync?: boolean; fromStorageService?: boolean } = {} + { + actionSource, + uninstalledAt, + }: { actionSource: ActionSourceType; uninstalledAt?: number } ): UninstallStickerPackAction { return { type: 'stickers/UNINSTALL_STICKER_PACK', - payload: doUninstallStickerPack(packId, packKey, options), + payload: doUninstallStickerPack(packId, packKey, { + actionSource, + uninstalledAt, + }), }; } async function doUninstallStickerPack( packId: string, packKey: string, - options: { fromSync?: boolean; fromStorageService?: boolean } = {} + { + actionSource, + uninstalledAt = Date.now(), + }: { actionSource: ActionSourceType; uninstalledAt?: number } ): Promise { - const { fromSync = false, fromStorageService = false } = options; - - const timestamp = Date.now(); - await DataWriter.uninstallStickerPack(packId, timestamp); + const changed = await DataWriter.uninstallStickerPack(packId, uninstalledAt); // If there are no more references, it should be removed await maybeDeletePack(packId); - if (!fromSync && !fromStorageService) { + if (actionSource === 'ui') { // Kick this off, but don't wait for it - void sendStickerPackSync(packId, packKey, false); + drop(sendStickerPackSync(packId, packKey, false)); } - if (!fromStorageService) { + if ( + // Don't cause storageService loop + actionSource !== 'storageService' && + // Stickers downloaded on startup should already be synced + actionSource !== 'startup' && + changed + ) { storageServiceUploadJob({ reason: 'doUninstallStickerPack' }); } @@ -302,7 +316,7 @@ async function doUninstallStickerPack( return { packId, - fromSync, + actionSource, status: 'downloaded', installedAt: undefined, recentStickers: recentStickers.map(item => ({ @@ -438,7 +452,8 @@ export function reducer( action.type === 'stickers/UNINSTALL_STICKER_PACK_FULFILLED' ) { const { payload } = action; - const { fromSync, installedAt, packId, status, recentStickers } = payload; + const { actionSource, installedAt, packId, status, recentStickers } = + payload; const { packs } = state; const existingPack = packs[packId]; @@ -453,7 +468,7 @@ export function reducer( } const isBlessed = state.blessedPacks[packId]; - const installedPack = !fromSync && !isBlessed ? packId : null; + const installedPack = actionSource === 'ui' && !isBlessed ? packId : null; return { ...state, diff --git a/ts/types/Stickers.ts b/ts/types/Stickers.ts index 0a0cf2af03..9c43e41ac1 100644 --- a/ts/types/Stickers.ts +++ b/ts/types/Stickers.ts @@ -32,6 +32,12 @@ import { isNotNil } from '../util/isNotNil'; import { encryptLegacyAttachment } from '../util/encryptLegacyAttachment'; import { AttachmentDisposition } from '../util/getLocalAttachmentUrl'; +export type ActionSourceType = + | 'startup' + | 'syncMessage' + | 'storageService' + | 'ui'; + export type StickerType = { packId: string; stickerId: number; @@ -265,6 +271,7 @@ export function downloadQueuedPacks(): void { downloadStickerPack(id, key, { finalStatus: status, suppressError: true, + actionSource: 'startup', }) ); } @@ -634,9 +641,7 @@ export async function downloadEphemeralPack( } export type DownloadStickerPackOptions = Readonly<{ - fromSync?: boolean; - fromStorageService?: boolean; - fromBackup?: boolean; + actionSource: ActionSourceType; finalStatus?: StickerPackStatusType; suppressError?: boolean; }>; @@ -644,7 +649,7 @@ export type DownloadStickerPackOptions = Readonly<{ export async function downloadStickerPack( packId: string, packKey: string, - options: DownloadStickerPackOptions = {} + options: DownloadStickerPackOptions ): Promise { // This will ensure that only one download process is in progress at any given time return downloadQueue.add(async () => { @@ -664,9 +669,7 @@ async function doDownloadStickerPack( packKey: string, { finalStatus = 'downloaded', - fromSync = false, - fromStorageService = false, - fromBackup = false, + actionSource, suppressError = false, }: DownloadStickerPackOptions ): Promise { @@ -788,9 +791,11 @@ async function doDownloadStickerPack( status: 'pending', createdAt: Date.now(), stickers: {}, - storageNeedsSync: !fromStorageService && !fromBackup, title: proto.title ?? '', author: proto.author ?? '', + + // Redux handles these + storageNeedsSync: false, }; await DataWriter.createOrUpdateStickerPack(pack); stickerPackAdded(pack); @@ -865,9 +870,7 @@ async function doDownloadStickerPack( // No-op } else if (finalStatus === 'installed') { await installStickerPack(packId, packKey, { - fromSync, - fromStorageService, - fromBackup, + actionSource, }); } else { // Mark the pack as complete diff --git a/ts/util/createIPCEvents.ts b/ts/util/createIPCEvents.ts index 1a33c8bdcd..b22d3a3e39 100644 --- a/ts/util/createIPCEvents.ts +++ b/ts/util/createIPCEvents.ts @@ -704,6 +704,7 @@ export function createIPCEvents( installStickerPack: async (packId, key) => { void Stickers.downloadStickerPack(packId, key, { finalStatus: 'installed', + actionSource: 'ui', }); },