// // Copyright 2024 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import GRDB /// This store holds a record of sticker packs that have been restored from /// a backup, but whose full data has not been downloaded. /// Post-restore, items listed here will be asynchronously passed to /// StickerManager, downloaded, and persisted as usable StickerPack objects. public protocol BackupStickerPackDownloadStore { /// "Enqueue" a sticker pack from a backup for download. /// Doesn't actually trigger a download; this is delegated to the TaskQueueLoader /// in StickerManager func enqueue( packId: Data, packKey: Data, tx: DBWriteTransaction ) throws /// Read rows off the queue one by one, calling the block for each. func iterateAllEnqueued( tx: DBReadTransaction, block: (QueuedBackupStickerPackDownload ) throws -> Void) throws /// Return the top `count` rows of the download queue. func peek( count: UInt, tx: DBReadTransaction ) throws -> [QueuedBackupStickerPackDownload] /// Remove the record from the download queue. func removeRecordFromQueue( record: QueuedBackupStickerPackDownload, tx: DBWriteTransaction ) throws } public class BackupStickerPackDownloadStoreImpl: BackupStickerPackDownloadStore { public typealias Record = QueuedBackupStickerPackDownload public func enqueue(packId: Data, packKey: Data, tx: DBWriteTransaction) throws { let db = tx.database var record = Record(packId: packId, packKey: packKey) // If this record is already in the queue, don't insert a second copy if let _ = try QueuedAttachmentDownloadRecord .filter(Column(Record.CodingKeys.packId) == packId) .fetchOne(db) { return } try record.insert(db) } public func iterateAllEnqueued( tx: DBReadTransaction, block: (QueuedBackupStickerPackDownload) throws -> Void ) throws { let db = tx.database let cursor = try Record .order([Column(Record.CodingKeys.id).desc]) .fetchCursor(db) while let record = try cursor.next() { try block(record) } } public func peek( count: UInt, tx: DBReadTransaction ) throws -> [QueuedBackupStickerPackDownload] { let db = tx.database return try Record .order([Column(Record.CodingKeys.id).asc]) .limit(Int(count)) .fetchAll(db) } public func removeRecordFromQueue( record: QueuedBackupStickerPackDownload, tx: DBWriteTransaction ) throws { let db = tx.database try Record .filter(Column(Record.CodingKeys.id) == record.id) .deleteAll(db) } }