Uncodegen StickerPack
This commit is contained in:
parent
a96689a6c6
commit
24643f3386
@ -672,6 +672,7 @@
|
||||
503C2F432977752B00217527 /* OWSURLSessionEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 503C2F422977752B00217527 /* OWSURLSessionEndpoint.swift */; };
|
||||
50423CA42BBF427900DCB8F5 /* StaleProfileFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50423CA32BBF427900DCB8F5 /* StaleProfileFetcher.swift */; };
|
||||
504271B62BB4C54500E33C01 /* SystemContact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504271B52BB4C54500E33C01 /* SystemContact.swift */; };
|
||||
50428EA52F2BD81700B0C969 /* StickerPackItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50428EA42F2BD81700B0C969 /* StickerPackItem.swift */; };
|
||||
5042EAA3287F96FB00C9B19F /* VisibleBadgeResolverTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5042EAA2287F96FB00C9B19F /* VisibleBadgeResolverTest.swift */; };
|
||||
50438A8E2ECBBDF600FCB28F /* BackupRefreshManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B975452E43A4AA00E20364 /* BackupRefreshManager.swift */; };
|
||||
50438A8F2ECBBE1D00FCB28F /* BackupRefreshManagerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04B975472E43BFE000E20364 /* BackupRefreshManagerTest.swift */; };
|
||||
@ -745,6 +746,7 @@
|
||||
507C07402F116E9200ECFEFA /* NormalizedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507C073F2F116E9200ECFEFA /* NormalizedImage.swift */; };
|
||||
507C6AF52ED10BD900A7C74C /* RegisteredState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507C6AF42ED10BD900A7C74C /* RegisteredState.swift */; };
|
||||
507CD5E529660D5100E47DAC /* ServiceId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507CD5E429660D5100E47DAC /* ServiceId.swift */; };
|
||||
507F4F0E2F2A86F8000E6D7D /* StickerPackRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 507F4F0D2F2A86F8000E6D7D /* StickerPackRecord.swift */; };
|
||||
508622AD2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508622AC2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift */; };
|
||||
508C72242C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508C72232C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift */; };
|
||||
508F0346296F72F4001D88D0 /* CustomCellBackgroundColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508F0345296F72F4001D88D0 /* CustomCellBackgroundColor.swift */; };
|
||||
@ -3595,14 +3597,11 @@
|
||||
F9C5CC0B289453B300548EEE /* StickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C919289453B100548EEE /* StickerManager.swift */; };
|
||||
F9C5CC0C289453B300548EEE /* CDNDownloadOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91A289453B100548EEE /* CDNDownloadOperation.swift */; };
|
||||
F9C5CC0D289453B300548EEE /* StickerMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91B289453B100548EEE /* StickerMetadata.swift */; };
|
||||
F9C5CC0E289453B300548EEE /* StickerPack+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91C289453B100548EEE /* StickerPack+SDS.swift */; };
|
||||
F9C5CC0F289453B300548EEE /* StickerPack.h in Headers */ = {isa = PBXBuildFile; fileRef = F9C5C91D289453B100548EEE /* StickerPack.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
F9C5CC11289453B300548EEE /* DefaultStickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C91F289453B100548EEE /* DefaultStickers.swift */; };
|
||||
F9C5CC12289453B300548EEE /* InstalledSticker+SDS.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C920289453B100548EEE /* InstalledSticker+SDS.swift */; };
|
||||
F9C5CC13289453B300548EEE /* StickerInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C921289453B100548EEE /* StickerInfo.m */; };
|
||||
F9C5CC14289453B300548EEE /* DownloadStickerOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C922289453B100548EEE /* DownloadStickerOperation.swift */; };
|
||||
F9C5CC15289453B300548EEE /* InstalledSticker.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C923289453B100548EEE /* InstalledSticker.m */; };
|
||||
F9C5CC17289453B300548EEE /* StickerPack.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C925289453B100548EEE /* StickerPack.m */; };
|
||||
F9C5CC18289453B300548EEE /* DownloadStickerPackOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C926289453B100548EEE /* DownloadStickerPackOperation.swift */; };
|
||||
F9C5CC19289453B300548EEE /* MessageSticker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C927289453B100548EEE /* MessageSticker.swift */; };
|
||||
F9C5CC1B289453B300548EEE /* StickerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9C5C929289453B100548EEE /* StickerError.swift */; };
|
||||
@ -4835,6 +4834,7 @@
|
||||
503C2F422977752B00217527 /* OWSURLSessionEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSURLSessionEndpoint.swift; sourceTree = "<group>"; };
|
||||
50423CA32BBF427900DCB8F5 /* StaleProfileFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaleProfileFetcher.swift; sourceTree = "<group>"; };
|
||||
504271B52BB4C54500E33C01 /* SystemContact.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemContact.swift; sourceTree = "<group>"; };
|
||||
50428EA42F2BD81700B0C969 /* StickerPackItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerPackItem.swift; sourceTree = "<group>"; };
|
||||
5042EAA2287F96FB00C9B19F /* VisibleBadgeResolverTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisibleBadgeResolverTest.swift; sourceTree = "<group>"; };
|
||||
50468F2829EE130A00948E02 /* InteractionStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionStore.swift; sourceTree = "<group>"; };
|
||||
50468F2A29EE19C300948E02 /* PhoneNumberChangedMessageInserterTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneNumberChangedMessageInserterTest.swift; sourceTree = "<group>"; };
|
||||
@ -4900,6 +4900,7 @@
|
||||
507D614B2BE433EE00DA7BA3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = translations/be.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
507D614C2BE433EE00DA7BA3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = translations/be.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
507D614D2BE433EE00DA7BA3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = be; path = translations/be.lproj/PluralAware.stringsdict; sourceTree = "<group>"; };
|
||||
507F4F0D2F2A86F8000E6D7D /* StickerPackRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerPackRecord.swift; sourceTree = "<group>"; };
|
||||
508347052AABBF9900DD2EC0 /* ProfileManagerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileManagerTest.swift; sourceTree = "<group>"; };
|
||||
508622AC2D026F5200931BF9 /* CanonicalPhoneNumberTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CanonicalPhoneNumberTest.swift; sourceTree = "<group>"; };
|
||||
508C72232C2DFCB2000811F3 /* OWSOutgoingResendResponseTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSOutgoingResendResponseTest.swift; sourceTree = "<group>"; };
|
||||
@ -7794,14 +7795,11 @@
|
||||
F9C5C919289453B100548EEE /* StickerManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerManager.swift; sourceTree = "<group>"; };
|
||||
F9C5C91A289453B100548EEE /* CDNDownloadOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CDNDownloadOperation.swift; sourceTree = "<group>"; };
|
||||
F9C5C91B289453B100548EEE /* StickerMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerMetadata.swift; sourceTree = "<group>"; };
|
||||
F9C5C91C289453B100548EEE /* StickerPack+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StickerPack+SDS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C91D289453B100548EEE /* StickerPack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StickerPack.h; sourceTree = "<group>"; };
|
||||
F9C5C91F289453B100548EEE /* DefaultStickers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultStickers.swift; sourceTree = "<group>"; };
|
||||
F9C5C920289453B100548EEE /* InstalledSticker+SDS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "InstalledSticker+SDS.swift"; sourceTree = "<group>"; };
|
||||
F9C5C921289453B100548EEE /* StickerInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StickerInfo.m; sourceTree = "<group>"; };
|
||||
F9C5C922289453B100548EEE /* DownloadStickerOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadStickerOperation.swift; sourceTree = "<group>"; };
|
||||
F9C5C923289453B100548EEE /* InstalledSticker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InstalledSticker.m; sourceTree = "<group>"; };
|
||||
F9C5C925289453B100548EEE /* StickerPack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = StickerPack.m; sourceTree = "<group>"; };
|
||||
F9C5C926289453B100548EEE /* DownloadStickerPackOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DownloadStickerPackOperation.swift; sourceTree = "<group>"; };
|
||||
F9C5C927289453B100548EEE /* MessageSticker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageSticker.swift; sourceTree = "<group>"; };
|
||||
F9C5C929289453B100548EEE /* StickerError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StickerError.swift; sourceTree = "<group>"; };
|
||||
@ -14545,10 +14543,9 @@
|
||||
F9C5C921289453B100548EEE /* StickerInfo.m */,
|
||||
F9C5C919289453B100548EEE /* StickerManager.swift */,
|
||||
F9C5C91B289453B100548EEE /* StickerMetadata.swift */,
|
||||
F9C5C91C289453B100548EEE /* StickerPack+SDS.swift */,
|
||||
F9C5C91D289453B100548EEE /* StickerPack.h */,
|
||||
F9C5C925289453B100548EEE /* StickerPack.m */,
|
||||
F9479EF7293D212C003B3503 /* StickerPackInfo.swift */,
|
||||
50428EA42F2BD81700B0C969 /* StickerPackItem.swift */,
|
||||
507F4F0D2F2A86F8000E6D7D /* StickerPackRecord.swift */,
|
||||
);
|
||||
path = Stickers;
|
||||
sourceTree = "<group>";
|
||||
@ -15303,7 +15300,6 @@
|
||||
F9C5C8A7289451B900548EEE /* SignalServiceKit.h in Headers */,
|
||||
F9C5CD2C289453B300548EEE /* SSKAccessors+SDS.h in Headers */,
|
||||
F9C5CC1C289453B300548EEE /* StickerInfo.h in Headers */,
|
||||
F9C5CC0F289453B300548EEE /* StickerPack.h in Headers */,
|
||||
668A01142C2B6077007B8808 /* Threading.h in Headers */,
|
||||
F9C5CBC8289453B300548EEE /* TSCall.h in Headers */,
|
||||
F9C5CCD1289453B300548EEE /* TSContactThread.h in Headers */,
|
||||
@ -19391,9 +19387,9 @@
|
||||
F9C5CC13289453B300548EEE /* StickerInfo.m in Sources */,
|
||||
F9C5CC0B289453B300548EEE /* StickerManager.swift in Sources */,
|
||||
F9C5CC0D289453B300548EEE /* StickerMetadata.swift in Sources */,
|
||||
F9C5CC0E289453B300548EEE /* StickerPack+SDS.swift in Sources */,
|
||||
F9C5CC17289453B300548EEE /* StickerPack.m in Sources */,
|
||||
F9479EF8293D212C003B3503 /* StickerPackInfo.swift in Sources */,
|
||||
50428EA52F2BD81700B0C969 /* StickerPackItem.swift in Sources */,
|
||||
507F4F0E2F2A86F8000E6D7D /* StickerPackRecord.swift in Sources */,
|
||||
500CC1EB2F15D1E5001A86F7 /* StickerPackSyncMessage.swift in Sources */,
|
||||
725465532BA0282D00EABFD2 /* StorageService+GroupsV2.swift in Sources */,
|
||||
F9C5CCA3289453B300548EEE /* StorageService.pb.swift in Sources */,
|
||||
|
||||
@ -140,8 +140,8 @@ public class ManageStickersViewController: OWSTableViewController2 {
|
||||
updateMapWithOldSources(&oldTransientSources, availableBuiltInStickerPackSources)
|
||||
updateMapWithOldSources(&oldTransientSources, knownStickerPackSources)
|
||||
|
||||
var installedStickerPacks = [StickerPack]()
|
||||
var availableBuiltInStickerPacks = [StickerPack]()
|
||||
var installedStickerPacks = [StickerPackRecord]()
|
||||
var availableBuiltInStickerPacks = [StickerPackRecord]()
|
||||
var availableKnownStickerPacksFromMessages = [DatedStickerPackInfo]()
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
let allPacks = StickerManager.allStickerPacks(transaction: transaction)
|
||||
@ -521,7 +521,7 @@ public class ManageStickersViewController: OWSTableViewController2 {
|
||||
self.sendMessageFlow = sendMessageFlow
|
||||
}
|
||||
|
||||
private func install(stickerPack: StickerPack) {
|
||||
private func install(stickerPack: StickerPackRecord) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
let modalVC = ModalActivityIndicatorViewController(canCancel: false, presentationDelay: 0)
|
||||
|
||||
@ -60,7 +60,7 @@ public class BackupArchiveStickerPackArchiver: BackupArchiveProtoStreamWriter {
|
||||
var handledPacks = Set<Data>()
|
||||
|
||||
func archiveInstalledStickerPack(
|
||||
_ installedStickerPack: StickerPack,
|
||||
_ installedStickerPack: StickerPackRecord,
|
||||
_ frameBencher: BackupArchive.Bencher.FrameBencher,
|
||||
) {
|
||||
autoreleasepool {
|
||||
@ -88,12 +88,11 @@ public class BackupArchiveStickerPackArchiver: BackupArchiveProtoStreamWriter {
|
||||
}
|
||||
}
|
||||
|
||||
func enumerateStickerPackRecord(tx: DBReadTransaction, block: (StickerPack) throws -> Void) throws {
|
||||
func enumerateStickerPackRecord(tx: DBReadTransaction, block: (StickerPackRecord) throws -> Void) throws {
|
||||
let cursor = try StickerPackRecord
|
||||
.filter(Column(StickerPackRecord.CodingKeys.isInstalled) == true)
|
||||
.fetchCursor(tx.database)
|
||||
while let next = try cursor.next() {
|
||||
let stickerPack = try StickerPack.fromRecord(next)
|
||||
while let stickerPack = try cursor.next() {
|
||||
try block(stickerPack)
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,13 +6,13 @@
|
||||
import Foundation
|
||||
|
||||
enum DownloadStickerPackOperation {
|
||||
static func run(stickerPackInfo: StickerPackInfo) async throws -> StickerPack {
|
||||
static func run(stickerPackInfo: StickerPackInfo) async throws -> StickerPackRecord {
|
||||
return try await Retry.performWithBackoff(maxAttempts: 4) {
|
||||
return try await self._run(stickerPackInfo: stickerPackInfo)
|
||||
}
|
||||
}
|
||||
|
||||
private static func _run(stickerPackInfo: StickerPackInfo) async throws -> StickerPack {
|
||||
private static func _run(stickerPackInfo: StickerPackInfo) async throws -> StickerPackRecord {
|
||||
owsAssertDebug(stickerPackInfo.packId.count > 0)
|
||||
owsAssertDebug(stickerPackInfo.packKey.count > 0)
|
||||
|
||||
@ -54,7 +54,7 @@ enum DownloadStickerPackOperation {
|
||||
}
|
||||
}
|
||||
|
||||
private static func parseStickerPackManifest(stickerPackInfo: StickerPackInfo, manifestData: Data) throws -> StickerPack {
|
||||
private static func parseStickerPackManifest(stickerPackInfo: StickerPackInfo, manifestData: Data) throws -> StickerPackRecord {
|
||||
owsAssertDebug(manifestData.count > 0)
|
||||
|
||||
let manifestProto: SSKProtoPack
|
||||
@ -79,8 +79,7 @@ enum DownloadStickerPackOperation {
|
||||
}
|
||||
let cover = manifestCover ?? firstItem
|
||||
|
||||
let stickerPack = StickerPack(info: stickerPackInfo, title: title, author: author, cover: cover, stickers: items)
|
||||
return stickerPack
|
||||
return StickerPackRecord(info: stickerPackInfo, title: title, author: author, cover: cover, items: items)
|
||||
}
|
||||
|
||||
private static func parseOptionalString(_ value: String?) -> String? {
|
||||
|
||||
@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@property (nonatomic, readonly) NSData *packKey;
|
||||
@property (nonatomic, readonly) UInt32 stickerId;
|
||||
|
||||
- (instancetype)initWithPackId:(NSData *)packId packKey:(NSData *)packKey stickerId:(UInt32)stickerId;
|
||||
- (instancetype)initWithPackId:(nullable NSData *)packId packKey:(nullable NSData *)packKey stickerId:(UInt32)stickerId;
|
||||
|
||||
- (NSString *)asKey;
|
||||
+ (NSString *)keyWithPackId:(NSData *)packId stickerId:(UInt32)stickerId;
|
||||
|
||||
@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation StickerInfo
|
||||
|
||||
- (instancetype)initWithPackId:(NSData *)packId packKey:(NSData *)packKey stickerId:(UInt32)stickerId
|
||||
- (instancetype)initWithPackId:(nullable NSData *)packId packKey:(nullable NSData *)packKey stickerId:(UInt32)stickerId
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
|
||||
@ -160,19 +160,19 @@ public class StickerManager: NSObject {
|
||||
|
||||
// MARK: - Sticker Packs
|
||||
|
||||
public class func allStickerPacks() -> [StickerPack] {
|
||||
var result = [StickerPack]()
|
||||
public class func allStickerPacks() -> [StickerPackRecord] {
|
||||
var result = [StickerPackRecord]()
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
result += allStickerPacks(transaction: transaction)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public class func allStickerPacks(transaction: DBReadTransaction) -> [StickerPack] {
|
||||
return StickerPack.anyFetchAll(transaction: transaction)
|
||||
public class func allStickerPacks(transaction: DBReadTransaction) -> [StickerPackRecord] {
|
||||
return StickerPackRecord.anyFetchAll(transaction: transaction)
|
||||
}
|
||||
|
||||
public class func installedStickerPacks(transaction: DBReadTransaction) -> [StickerPack] {
|
||||
public class func installedStickerPacks(transaction: DBReadTransaction) -> [StickerPackRecord] {
|
||||
return allStickerPacks(transaction: transaction).filter {
|
||||
$0.isInstalled
|
||||
}
|
||||
@ -204,7 +204,7 @@ public class StickerManager: NSObject {
|
||||
if shouldRemove {
|
||||
uninstallSticker(stickerInfo: stickerPack.coverInfo, transaction: transaction)
|
||||
|
||||
for stickerInfo in stickerPack.stickerInfos {
|
||||
for stickerInfo in stickerPack.stickerInfos() {
|
||||
if stickerInfo == stickerPack.coverInfo {
|
||||
// Don't uninstall the cover for saved packs.
|
||||
continue
|
||||
@ -214,7 +214,7 @@ public class StickerManager: NSObject {
|
||||
|
||||
stickerPack.anyRemove(transaction: transaction)
|
||||
} else {
|
||||
stickerPack.update(withIsInstalled: false, transaction: transaction)
|
||||
stickerPack.updateWith(isInstalled: false, tx: transaction)
|
||||
}
|
||||
|
||||
if wasLocallyInitiated {
|
||||
@ -231,7 +231,7 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
public class func installStickerPack(
|
||||
stickerPack: StickerPack,
|
||||
stickerPack: StickerPackRecord,
|
||||
wasLocallyInitiated: Bool,
|
||||
transaction: DBWriteTransaction,
|
||||
) {
|
||||
@ -243,15 +243,15 @@ public class StickerManager: NSObject {
|
||||
)
|
||||
}
|
||||
|
||||
public class func fetchStickerPack(stickerPackInfo: StickerPackInfo) -> StickerPack? {
|
||||
public class func fetchStickerPack(stickerPackInfo: StickerPackInfo) -> StickerPackRecord? {
|
||||
return SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
return fetchStickerPack(stickerPackInfo: stickerPackInfo, transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
public class func fetchStickerPack(stickerPackInfo: StickerPackInfo, transaction: DBReadTransaction) -> StickerPack? {
|
||||
let uniqueId = StickerPack.uniqueId(for: stickerPackInfo)
|
||||
return StickerPack.anyFetch(uniqueId: uniqueId, transaction: transaction)
|
||||
public class func fetchStickerPack(stickerPackInfo: StickerPackInfo, transaction: DBReadTransaction) -> StickerPackRecord? {
|
||||
let uniqueId = StickerPackRecord.uniqueId(forStickerPackInfo: stickerPackInfo)
|
||||
return StickerPackRecord.anyFetch(uniqueId: uniqueId, transaction: transaction)
|
||||
}
|
||||
|
||||
private class func tryToDownloadAndSaveStickerPack(
|
||||
@ -270,7 +270,7 @@ public class StickerManager: NSObject {
|
||||
|
||||
private let packOperationQueue = ConcurrentTaskQueue(concurrentLimit: 3)
|
||||
|
||||
private func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise<StickerPack> {
|
||||
private func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise<StickerPackRecord> {
|
||||
return Promise.wrapAsync { [packOperationQueue] in
|
||||
return try await packOperationQueue.run {
|
||||
return try await DownloadStickerPackOperation.run(stickerPackInfo: stickerPackInfo)
|
||||
@ -279,12 +279,12 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
// This method is public so that we can download "transient" (uninstalled) sticker packs.
|
||||
public class func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise<StickerPack> {
|
||||
public class func tryToDownloadStickerPack(stickerPackInfo: StickerPackInfo) -> Promise<StickerPackRecord> {
|
||||
return SSKEnvironment.shared.stickerManagerRef.tryToDownloadStickerPack(stickerPackInfo: stickerPackInfo)
|
||||
}
|
||||
|
||||
private class func upsertStickerPack(
|
||||
stickerPack: StickerPack,
|
||||
stickerPack: StickerPackRecord,
|
||||
installMode: InstallMode,
|
||||
wasLocallyInitiated: Bool,
|
||||
) {
|
||||
@ -299,7 +299,7 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
private class func upsertStickerPack(
|
||||
stickerPack stickerPackParam: StickerPack,
|
||||
stickerPack stickerPackParam: StickerPackRecord,
|
||||
installMode: InstallMode,
|
||||
wasLocallyInitiated: Bool,
|
||||
transaction: DBWriteTransaction,
|
||||
@ -307,15 +307,15 @@ public class StickerManager: NSObject {
|
||||
// If we re-insert a sticker pack, make sure that it
|
||||
// has a new row id.
|
||||
_ = stickerPackParam as NSCopying
|
||||
let stickerPack = stickerPackParam.copy() as! StickerPack
|
||||
stickerPack.clearRowId()
|
||||
let stickerPack = stickerPackParam.copy() as! StickerPackRecord
|
||||
stickerPack.id = nil
|
||||
|
||||
let oldCopy = fetchStickerPack(stickerPackInfo: stickerPack.info, transaction: transaction)
|
||||
let wasSaved = oldCopy != nil
|
||||
|
||||
// Preserve old mutable state.
|
||||
if let oldCopy {
|
||||
stickerPack.update(withIsInstalled: oldCopy.isInstalled, transaction: transaction)
|
||||
stickerPack.updateWith(isInstalled: oldCopy.isInstalled, tx: transaction)
|
||||
} else {
|
||||
stickerPack.anyInsert(transaction: transaction)
|
||||
}
|
||||
@ -363,7 +363,7 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
private class func markSavedStickerPackAsInstalled(
|
||||
stickerPack: StickerPack,
|
||||
stickerPack: StickerPackRecord,
|
||||
wasLocallyInitiated: Bool,
|
||||
transaction: DBWriteTransaction,
|
||||
) -> Promise<Void> {
|
||||
@ -371,7 +371,7 @@ public class StickerManager: NSObject {
|
||||
return .value(())
|
||||
}
|
||||
|
||||
stickerPack.update(withIsInstalled: true, transaction: transaction)
|
||||
stickerPack.updateWith(isInstalled: true, tx: transaction)
|
||||
|
||||
let promise = installStickerPackContents(stickerPack: stickerPack, transaction: transaction)
|
||||
|
||||
@ -386,7 +386,7 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
private class func installStickerPackContents(
|
||||
stickerPack: StickerPack,
|
||||
stickerPack: StickerPackRecord,
|
||||
transaction: DBReadTransaction,
|
||||
onlyInstallCover: Bool = false,
|
||||
) -> Promise<Void> {
|
||||
@ -456,7 +456,7 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
public class func installedStickers(
|
||||
forStickerPack stickerPack: StickerPack,
|
||||
forStickerPack stickerPack: StickerPackRecord,
|
||||
verifyExists: Bool,
|
||||
) -> [StickerInfo] {
|
||||
return SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
@ -469,12 +469,12 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
public class func installedStickers(
|
||||
forStickerPack stickerPack: StickerPack,
|
||||
forStickerPack stickerPack: StickerPackRecord,
|
||||
verifyExists: Bool,
|
||||
transaction: DBReadTransaction,
|
||||
) -> [StickerInfo] {
|
||||
var result = [StickerInfo]()
|
||||
for stickerInfo in stickerPack.stickerInfos {
|
||||
for stickerInfo in stickerPack.stickerInfos() {
|
||||
let uniqueId = InstalledSticker.uniqueId(for: stickerInfo)
|
||||
guard let installedSticker = InstalledSticker.anyFetch(uniqueId: uniqueId, transaction: transaction) else {
|
||||
continue
|
||||
@ -723,11 +723,11 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
private class func tryToDownloadAndInstallSticker(
|
||||
stickerPack: StickerPack,
|
||||
stickerPack: StickerPackRecord,
|
||||
item: StickerPackItem,
|
||||
transaction: DBReadTransaction,
|
||||
) -> Promise<Bool> {
|
||||
let stickerInfo: StickerInfo = item.stickerInfo(with: stickerPack)
|
||||
let stickerInfo: StickerInfo = item.stickerInfoWith(stickerPack: stickerPack)
|
||||
let emojiString = item.emojiString
|
||||
|
||||
guard !self.isStickerInstalled(stickerInfo: stickerInfo, transaction: transaction) else {
|
||||
@ -1001,7 +1001,7 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
public class func ensureDownloadsAsync(forStickerPack stickerPack: StickerPack) -> Promise<Void> {
|
||||
public class func ensureDownloadsAsync(forStickerPack stickerPack: StickerPackRecord) -> Promise<Void> {
|
||||
let (promise, future) = Promise<Void>.pending()
|
||||
DispatchQueue.global().async {
|
||||
SSKEnvironment.shared.databaseStorageRef.read { transaction in
|
||||
@ -1018,7 +1018,7 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private class func ensureDownloads(forStickerPack stickerPack: StickerPack, transaction: DBReadTransaction) -> Promise<Void> {
|
||||
private class func ensureDownloads(forStickerPack stickerPack: StickerPackRecord, transaction: DBReadTransaction) -> Promise<Void> {
|
||||
// TODO: As an optimization, we could flag packs as "complete" if we know all
|
||||
// of their stickers are installed.
|
||||
|
||||
@ -1054,11 +1054,11 @@ public class StickerManager: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
private static func fetchOrphanedPacksAndStickers(tx: DBReadTransaction) -> ([StickerPack], [InstalledSticker]) {
|
||||
var stickerPacks = [String: StickerPack]()
|
||||
var packsToRemove = [StickerPack]()
|
||||
private static func fetchOrphanedPacksAndStickers(tx: DBReadTransaction) -> ([StickerPackRecord], [InstalledSticker]) {
|
||||
var stickerPacks = [String: StickerPackRecord]()
|
||||
var packsToRemove = [StickerPackRecord]()
|
||||
|
||||
for stickerPack in StickerPack.anyFetchAll(transaction: tx) {
|
||||
for stickerPack in StickerPackRecord.anyFetchAll(transaction: tx) {
|
||||
if stickerPack.isInstalled || self.isDefaultStickerPack(packId: stickerPack.info.packId) {
|
||||
stickerPacks[stickerPack.info.asKey] = stickerPack
|
||||
} else {
|
||||
|
||||
@ -1,509 +0,0 @@
|
||||
//
|
||||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public import GRDB
|
||||
|
||||
// NOTE: This file is generated by /Scripts/sds_codegen/sds_generate.py.
|
||||
// Do not manually edit it, instead run `sds_codegen.sh`.
|
||||
|
||||
// MARK: - Record
|
||||
|
||||
public struct StickerPackRecord: SDSRecord {
|
||||
public weak var delegate: SDSRecordDelegate?
|
||||
|
||||
public var tableMetadata: SDSTableMetadata {
|
||||
StickerPackSerializer.table
|
||||
}
|
||||
|
||||
public static var databaseTableName: String {
|
||||
StickerPackSerializer.table.tableName
|
||||
}
|
||||
|
||||
public var id: Int64?
|
||||
|
||||
// This defines all of the columns used in the table
|
||||
// where this model (and any subclasses) are persisted.
|
||||
public let recordType: SDSRecordType?
|
||||
public let uniqueId: String
|
||||
|
||||
// Properties
|
||||
public let author: String?
|
||||
public let cover: Data
|
||||
public let dateCreated: Double
|
||||
public let info: Data
|
||||
public let isInstalled: Bool
|
||||
public let items: Data
|
||||
public let title: String?
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
|
||||
case id
|
||||
case recordType
|
||||
case uniqueId
|
||||
case author
|
||||
case cover
|
||||
case dateCreated
|
||||
case info
|
||||
case isInstalled
|
||||
case items
|
||||
case title
|
||||
}
|
||||
|
||||
public static func columnName(_ column: StickerPackRecord.CodingKeys, fullyQualified: Bool = false) -> String {
|
||||
fullyQualified ? "\(databaseTableName).\(column.rawValue)" : column.rawValue
|
||||
}
|
||||
|
||||
public func didInsert(with rowID: Int64, for column: String?) {
|
||||
guard let delegate = delegate else {
|
||||
owsFailDebug("Missing delegate.")
|
||||
return
|
||||
}
|
||||
delegate.updateRowId(rowID)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Row Initializer
|
||||
|
||||
public extension StickerPackRecord {
|
||||
static var databaseSelection: [SQLSelectable] {
|
||||
CodingKeys.allCases
|
||||
}
|
||||
|
||||
init(row: Row) {
|
||||
id = row[0]
|
||||
recordType = row[1].flatMap { SDSRecordType(rawValue: $0) }
|
||||
uniqueId = row[2]
|
||||
author = row[3]
|
||||
cover = row[4]
|
||||
dateCreated = row[5]
|
||||
info = row[6]
|
||||
isInstalled = row[7]
|
||||
items = row[8]
|
||||
title = row[9]
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StringInterpolation
|
||||
|
||||
public extension String.StringInterpolation {
|
||||
mutating func appendInterpolation(stickerPackColumn column: StickerPackRecord.CodingKeys) {
|
||||
appendLiteral(StickerPackRecord.columnName(column))
|
||||
}
|
||||
mutating func appendInterpolation(stickerPackColumnFullyQualified column: StickerPackRecord.CodingKeys) {
|
||||
appendLiteral(StickerPackRecord.columnName(column, fullyQualified: true))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Deserialization
|
||||
|
||||
extension StickerPack {
|
||||
// This method defines how to deserialize a model, given a
|
||||
// database row. The recordType column is used to determine
|
||||
// the corresponding model class.
|
||||
class func fromRecord(_ record: StickerPackRecord) throws -> StickerPack {
|
||||
|
||||
guard let recordId = record.id else { throw SDSError.missingRequiredField(fieldName: "id") }
|
||||
guard let recordType = record.recordType else { throw SDSError.missingRequiredField(fieldName: "recordType") }
|
||||
|
||||
switch recordType {
|
||||
case .stickerPack:
|
||||
|
||||
let uniqueId: String = record.uniqueId
|
||||
let author: String? = record.author
|
||||
let coverSerialized: Data = record.cover
|
||||
let cover: StickerPackItem = try SDSDeserialization.unarchivedObject(ofClass: StickerPackItem.self, from: coverSerialized)
|
||||
let dateCreatedInterval: Double = record.dateCreated
|
||||
let dateCreated: Date = SDSDeserialization.requiredDoubleAsDate(dateCreatedInterval, name: "dateCreated")
|
||||
let infoSerialized: Data = record.info
|
||||
let info: StickerPackInfo = try SDSDeserialization.unarchivedObject(ofClass: StickerPackInfo.self, from: infoSerialized)
|
||||
let isInstalled: Bool = record.isInstalled
|
||||
let itemsSerialized: Data = record.items
|
||||
let items: [StickerPackItem] = try SDSDeserialization.unarchivedArrayOfObjects(ofClass: StickerPackItem.self, from: itemsSerialized)
|
||||
let title: String? = record.title
|
||||
|
||||
return StickerPack(grdbId: recordId,
|
||||
uniqueId: uniqueId,
|
||||
author: author,
|
||||
cover: cover,
|
||||
dateCreated: dateCreated,
|
||||
info: info,
|
||||
isInstalled: isInstalled,
|
||||
items: items,
|
||||
title: title)
|
||||
|
||||
default:
|
||||
owsFailDebug("Unexpected record type: \(recordType)")
|
||||
throw SDSError.invalidValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SDSModel
|
||||
|
||||
extension StickerPack: SDSModel {
|
||||
public var serializer: SDSSerializer {
|
||||
// Any subclass can be cast to it's superclass,
|
||||
// so the order of this switch statement matters.
|
||||
// We need to do a "depth first" search by type.
|
||||
switch self {
|
||||
default:
|
||||
return StickerPackSerializer(model: self)
|
||||
}
|
||||
}
|
||||
|
||||
public func asRecord() -> SDSRecord {
|
||||
serializer.asRecord()
|
||||
}
|
||||
|
||||
public var sdsTableName: String {
|
||||
StickerPackRecord.databaseTableName
|
||||
}
|
||||
|
||||
public static var table: SDSTableMetadata {
|
||||
StickerPackSerializer.table
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - DeepCopyable
|
||||
|
||||
extension StickerPack: DeepCopyable {
|
||||
|
||||
public func deepCopy() throws -> AnyObject {
|
||||
guard let id = self.grdbId?.int64Value else {
|
||||
throw OWSAssertionError("Model missing grdbId.")
|
||||
}
|
||||
|
||||
// Any subclass can be cast to its superclass, so the order of these if
|
||||
// statements matters. We need to do a "depth first" search by type.
|
||||
|
||||
do {
|
||||
let modelToCopy = self
|
||||
assert(type(of: modelToCopy) == StickerPack.self)
|
||||
let uniqueId: String = modelToCopy.uniqueId
|
||||
let author: String? = modelToCopy.author
|
||||
let cover: StickerPackItem = try DeepCopies.deepCopy(modelToCopy.cover)
|
||||
let dateCreated: Date = modelToCopy.dateCreated
|
||||
let info: StickerPackInfo = try DeepCopies.deepCopy(modelToCopy.info)
|
||||
let isInstalled: Bool = modelToCopy.isInstalled
|
||||
let items: [StickerPackItem] = try DeepCopies.deepCopy(modelToCopy.items)
|
||||
let title: String? = modelToCopy.title
|
||||
|
||||
return StickerPack(grdbId: id,
|
||||
uniqueId: uniqueId,
|
||||
author: author,
|
||||
cover: cover,
|
||||
dateCreated: dateCreated,
|
||||
info: info,
|
||||
isInstalled: isInstalled,
|
||||
items: items,
|
||||
title: title)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Table Metadata
|
||||
|
||||
extension StickerPackSerializer {
|
||||
|
||||
// This defines all of the columns used in the table
|
||||
// where this model (and any subclasses) are persisted.
|
||||
static var idColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "id", columnType: .primaryKey) }
|
||||
static var recordTypeColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "recordType", columnType: .int64) }
|
||||
static var uniqueIdColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "uniqueId", columnType: .unicodeString, isUnique: true) }
|
||||
// Properties
|
||||
static var authorColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "author", columnType: .unicodeString, isOptional: true) }
|
||||
static var coverColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "cover", columnType: .blob) }
|
||||
static var dateCreatedColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "dateCreated", columnType: .double) }
|
||||
static var infoColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "info", columnType: .blob) }
|
||||
static var isInstalledColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "isInstalled", columnType: .int) }
|
||||
static var itemsColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "items", columnType: .blob) }
|
||||
static var titleColumn: SDSColumnMetadata { SDSColumnMetadata(columnName: "title", columnType: .unicodeString, isOptional: true) }
|
||||
|
||||
public static var table: SDSTableMetadata {
|
||||
SDSTableMetadata(
|
||||
tableName: "model_StickerPack",
|
||||
columns: [
|
||||
idColumn,
|
||||
recordTypeColumn,
|
||||
uniqueIdColumn,
|
||||
authorColumn,
|
||||
coverColumn,
|
||||
dateCreatedColumn,
|
||||
infoColumn,
|
||||
isInstalledColumn,
|
||||
itemsColumn,
|
||||
titleColumn,
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Save/Remove/Update
|
||||
|
||||
@objc
|
||||
public extension StickerPack {
|
||||
func anyInsert(transaction: DBWriteTransaction) {
|
||||
sdsSave(saveMode: .insert, transaction: transaction)
|
||||
}
|
||||
|
||||
// Avoid this method whenever feasible.
|
||||
//
|
||||
// If the record has previously been saved, this method does an overwriting
|
||||
// update of the corresponding row, otherwise if it's a new record, this
|
||||
// method inserts a new row.
|
||||
//
|
||||
// For performance, when possible, you should explicitly specify whether
|
||||
// you are inserting or updating rather than calling this method.
|
||||
func anyUpsert(transaction: DBWriteTransaction) {
|
||||
let isInserting: Bool
|
||||
if StickerPack.anyFetch(uniqueId: uniqueId, transaction: transaction) != nil {
|
||||
isInserting = false
|
||||
} else {
|
||||
isInserting = true
|
||||
}
|
||||
sdsSave(saveMode: isInserting ? .insert : .update, transaction: transaction)
|
||||
}
|
||||
|
||||
// This method is used by "updateWith..." methods.
|
||||
//
|
||||
// This model may be updated from many threads. We don't want to save
|
||||
// our local copy (this instance) since it may be out of date. We also
|
||||
// want to avoid re-saving a model that has been deleted. Therefore, we
|
||||
// use "updateWith..." methods to:
|
||||
//
|
||||
// a) Update a property of this instance.
|
||||
// b) If a copy of this model exists in the database, load an up-to-date copy,
|
||||
// and update and save that copy.
|
||||
// b) If a copy of this model _DOES NOT_ exist in the database, do _NOT_ save
|
||||
// this local instance.
|
||||
//
|
||||
// After "updateWith...":
|
||||
//
|
||||
// a) Any copy of this model in the database will have been updated.
|
||||
// b) The local property on this instance will always have been updated.
|
||||
// c) Other properties on this instance may be out of date.
|
||||
//
|
||||
// All mutable properties of this class have been made read-only to
|
||||
// prevent accidentally modifying them directly.
|
||||
//
|
||||
// This isn't a perfect arrangement, but in practice this will prevent
|
||||
// data loss and will resolve all known issues.
|
||||
func anyUpdate(transaction: DBWriteTransaction, block: (StickerPack) -> Void) {
|
||||
|
||||
block(self)
|
||||
|
||||
// If it's not saved, we don't expect to find it in the database, and we
|
||||
// won't save any changes we make back into the database.
|
||||
guard shouldBeSaved else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let dbCopy = type(of: self).anyFetch(uniqueId: uniqueId, transaction: transaction) else {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't apply the block twice to the same instance.
|
||||
// It's at least unnecessary and actually wrong for some blocks.
|
||||
// e.g. `block: { $0 in $0.someField++ }`
|
||||
if dbCopy !== self {
|
||||
block(dbCopy)
|
||||
}
|
||||
|
||||
dbCopy.sdsSave(saveMode: .update, transaction: transaction)
|
||||
}
|
||||
|
||||
// This method is an alternative to `anyUpdate(transaction:block:)` methods.
|
||||
//
|
||||
// We should generally use `anyUpdate` to ensure we're not unintentionally
|
||||
// clobbering other columns in the database when another concurrent update
|
||||
// has occurred.
|
||||
//
|
||||
// There are cases when this doesn't make sense, e.g. when we know we've
|
||||
// just loaded the model in the same transaction. In those cases it is
|
||||
// safe and faster to do a "overwriting" update
|
||||
func anyOverwritingUpdate(transaction: DBWriteTransaction) {
|
||||
sdsSave(saveMode: .update, transaction: transaction)
|
||||
}
|
||||
|
||||
func anyRemove(transaction: DBWriteTransaction) {
|
||||
sdsRemove(transaction: transaction)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - StickerPackCursor
|
||||
|
||||
@objc
|
||||
public class StickerPackCursor: NSObject, SDSCursor {
|
||||
private let transaction: DBReadTransaction
|
||||
private let cursor: RecordCursor<StickerPackRecord>
|
||||
|
||||
init(transaction: DBReadTransaction, cursor: RecordCursor<StickerPackRecord>) {
|
||||
self.transaction = transaction
|
||||
self.cursor = cursor
|
||||
}
|
||||
|
||||
public func next() throws -> StickerPack? {
|
||||
guard let record = try cursor.next() else {
|
||||
return nil
|
||||
}
|
||||
return try StickerPack.fromRecord(record)
|
||||
}
|
||||
|
||||
public func all() throws -> [StickerPack] {
|
||||
var result = [StickerPack]()
|
||||
while true {
|
||||
guard let model = try next() else {
|
||||
break
|
||||
}
|
||||
result.append(model)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Obj-C Fetch
|
||||
|
||||
@objc
|
||||
public extension StickerPack {
|
||||
@nonobjc
|
||||
class func grdbFetchCursor(transaction: DBReadTransaction) -> StickerPackCursor {
|
||||
let database = transaction.database
|
||||
return failIfThrows {
|
||||
let cursor = try StickerPackRecord.fetchCursor(database)
|
||||
return StickerPackCursor(transaction: transaction, cursor: cursor)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetches a single model by "unique id".
|
||||
class func anyFetch(uniqueId: String,
|
||||
transaction: DBReadTransaction) -> StickerPack? {
|
||||
assert(!uniqueId.isEmpty)
|
||||
|
||||
let sql = "SELECT * FROM \(StickerPackRecord.databaseTableName) WHERE \(stickerPackColumn: .uniqueId) = ?"
|
||||
return grdbFetchOne(sql: sql, arguments: [uniqueId], transaction: transaction)
|
||||
}
|
||||
|
||||
// Traverses all records.
|
||||
// Records are not visited in any particular order.
|
||||
class func anyEnumerate(
|
||||
transaction: DBReadTransaction,
|
||||
block: (StickerPack, UnsafeMutablePointer<ObjCBool>) -> Void
|
||||
) {
|
||||
anyEnumerate(transaction: transaction, batched: false, block: block)
|
||||
}
|
||||
|
||||
// Traverses all records.
|
||||
// Records are not visited in any particular order.
|
||||
class func anyEnumerate(
|
||||
transaction: DBReadTransaction,
|
||||
batched: Bool = false,
|
||||
block: (StickerPack, UnsafeMutablePointer<ObjCBool>) -> Void
|
||||
) {
|
||||
let batchSize = batched ? Batching.kDefaultBatchSize : 0
|
||||
anyEnumerate(transaction: transaction, batchSize: batchSize, block: block)
|
||||
}
|
||||
|
||||
// Traverses all records.
|
||||
// Records are not visited in any particular order.
|
||||
//
|
||||
// If batchSize > 0, the enumeration is performed in autoreleased batches.
|
||||
class func anyEnumerate(
|
||||
transaction: DBReadTransaction,
|
||||
batchSize: UInt,
|
||||
block: (StickerPack, UnsafeMutablePointer<ObjCBool>) -> Void
|
||||
) {
|
||||
let cursor = StickerPack.grdbFetchCursor(transaction: transaction)
|
||||
Batching.loop(batchSize: batchSize,
|
||||
loopBlock: { stop in
|
||||
do {
|
||||
guard let value = try cursor.next() else {
|
||||
stop.pointee = true
|
||||
return
|
||||
}
|
||||
block(value, stop)
|
||||
} catch let error {
|
||||
owsFailDebug("Couldn't fetch model: \(error)")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Does not order the results.
|
||||
class func anyFetchAll(transaction: DBReadTransaction) -> [StickerPack] {
|
||||
var result = [StickerPack]()
|
||||
anyEnumerate(transaction: transaction) { (model, _) in
|
||||
result.append(model)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
class func anyCount(transaction: DBReadTransaction) -> UInt {
|
||||
return StickerPackRecord.ows_fetchCount(transaction.database)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Swift Fetch
|
||||
|
||||
public extension StickerPack {
|
||||
class func grdbFetchCursor(sql: String,
|
||||
arguments: StatementArguments = StatementArguments(),
|
||||
transaction: DBReadTransaction) -> StickerPackCursor {
|
||||
return failIfThrows {
|
||||
let sqlRequest = SQLRequest<Void>(sql: sql, arguments: arguments, cached: true)
|
||||
let cursor = try StickerPackRecord.fetchCursor(transaction.database, sqlRequest)
|
||||
return StickerPackCursor(transaction: transaction, cursor: cursor)
|
||||
}
|
||||
}
|
||||
|
||||
class func grdbFetchOne(sql: String,
|
||||
arguments: StatementArguments = StatementArguments(),
|
||||
transaction: DBReadTransaction) -> StickerPack? {
|
||||
assert(!sql.isEmpty)
|
||||
|
||||
do {
|
||||
let sqlRequest = SQLRequest<Void>(sql: sql, arguments: arguments, cached: true)
|
||||
guard let record = try StickerPackRecord.fetchOne(transaction.database, sqlRequest) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return try StickerPack.fromRecord(record)
|
||||
} catch {
|
||||
owsFailDebug("error: \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SDSSerializer
|
||||
|
||||
// The SDSSerializer protocol specifies how to insert and update the
|
||||
// row that corresponds to this model.
|
||||
class StickerPackSerializer: SDSSerializer {
|
||||
|
||||
private let model: StickerPack
|
||||
public init(model: StickerPack) {
|
||||
self.model = model
|
||||
}
|
||||
|
||||
// MARK: - Record
|
||||
|
||||
func asRecord() -> SDSRecord {
|
||||
let id: Int64? = model.grdbId?.int64Value
|
||||
|
||||
let recordType: SDSRecordType = .stickerPack
|
||||
let uniqueId: String = model.uniqueId
|
||||
|
||||
// Properties
|
||||
let author: String? = model.author
|
||||
let cover: Data = requiredArchive(model.cover)
|
||||
let dateCreated: Double = archiveDate(model.dateCreated)
|
||||
let info: Data = requiredArchive(model.info)
|
||||
let isInstalled: Bool = model.isInstalled
|
||||
let items: Data = requiredArchive(model.items)
|
||||
let title: String? = model.title
|
||||
|
||||
return StickerPackRecord(delegate: model, id: id, recordType: recordType, uniqueId: uniqueId, author: author, cover: cover, dateCreated: dateCreated, info: info, isInstalled: isInstalled, items: items, title: title)
|
||||
}
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
//
|
||||
// Copyright 2019 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
@import Foundation;
|
||||
|
||||
#import <SignalServiceKit/BaseModel.h>
|
||||
#import <SignalServiceKit/StickerInfo.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class DBWriteTransaction;
|
||||
@class StickerPack;
|
||||
|
||||
@interface StickerPackItem : NSObject <NSSecureCoding, NSCopying>
|
||||
|
||||
@property (nonatomic, readonly) UInt32 stickerId;
|
||||
@property (nonatomic, readonly) NSString *emojiString;
|
||||
@property (nonatomic, readonly, nullable) NSString *contentType;
|
||||
|
||||
- (instancetype)initWithStickerId:(UInt32)stickerId
|
||||
emojiString:(NSString *)emojiString
|
||||
contentType:(nullable NSString *)contentType;
|
||||
|
||||
- (StickerInfo *)stickerInfoWithStickerPack:(StickerPack *)stickerPack;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface StickerPack : BaseModel <NSCopying>
|
||||
|
||||
@property (nonatomic, readonly) StickerPackInfo *info;
|
||||
|
||||
@property (nonatomic, readonly, nullable) NSString *title;
|
||||
@property (nonatomic, readonly, nullable) NSString *author;
|
||||
@property (nonatomic, readonly) StickerPackItem *cover;
|
||||
@property (nonatomic, readonly) NSArray<StickerPackItem *> *items;
|
||||
|
||||
// Convenience accessors.
|
||||
@property (nonatomic, readonly) NSData *packId;
|
||||
@property (nonatomic, readonly) NSData *packKey;
|
||||
@property (nonatomic, readonly) StickerInfo *coverInfo;
|
||||
@property (nonatomic, readonly) NSArray<StickerInfo *> *stickerInfos;
|
||||
|
||||
@property (nonatomic, readonly) NSDate *dateCreated;
|
||||
@property (nonatomic, readonly) BOOL isInstalled;
|
||||
|
||||
+ (instancetype)new NS_UNAVAILABLE;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_UNAVAILABLE;
|
||||
- (instancetype)initWithUniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId uniqueId:(NSString *)uniqueId NS_UNAVAILABLE;
|
||||
|
||||
- (instancetype)initWithInfo:(StickerPackInfo *)info
|
||||
title:(nullable NSString *)title
|
||||
author:(nullable NSString *)author
|
||||
cover:(StickerPackItem *)cover
|
||||
stickers:(NSArray<StickerPackItem *> *)items NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
author:(nullable NSString *)author
|
||||
cover:(StickerPackItem *)cover
|
||||
dateCreated:(NSDate *)dateCreated
|
||||
info:(StickerPackInfo *)info
|
||||
isInstalled:(BOOL)isInstalled
|
||||
items:(NSArray<StickerPackItem *> *)items
|
||||
title:(nullable NSString *)title
|
||||
NS_DESIGNATED_INITIALIZER NS_SWIFT_NAME(init(grdbId:uniqueId:author:cover:dateCreated:info:isInstalled:items:title:));
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
+ (NSString *)uniqueIdForStickerPackInfo:(StickerPackInfo *)info;
|
||||
|
||||
- (void)updateWithIsInstalled:(BOOL)isInstalled transaction:(DBWriteTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@ -1,276 +0,0 @@
|
||||
//
|
||||
// Copyright 2019 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
#import "StickerPack.h"
|
||||
#import <SignalServiceKit/SignalServiceKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation StickerPackItem
|
||||
|
||||
- (instancetype)initWithStickerId:(UInt32)stickerId
|
||||
emojiString:(NSString *)emojiString
|
||||
contentType:(nullable NSString *)contentType
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_stickerId = stickerId;
|
||||
_emojiString = emojiString;
|
||||
if (contentType.length > 0) {
|
||||
_contentType = contentType;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (BOOL)supportsSecureCoding
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)encodeWithCoder:(NSCoder *)coder
|
||||
{
|
||||
NSString *contentType = self.contentType;
|
||||
if (contentType != nil) {
|
||||
[coder encodeObject:contentType forKey:@"contentType"];
|
||||
}
|
||||
NSString *emojiString = self.emojiString;
|
||||
if (emojiString != nil) {
|
||||
[coder encodeObject:emojiString forKey:@"emojiString"];
|
||||
}
|
||||
[coder encodeObject:[self valueForKey:@"stickerId"] forKey:@"stickerId"];
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
self = [super init];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
self->_contentType = [coder decodeObjectOfClass:[NSString class] forKey:@"contentType"];
|
||||
self->_emojiString = [coder decodeObjectOfClass:[NSString class] forKey:@"emojiString"];
|
||||
self->_stickerId = [(NSNumber *)[coder decodeObjectOfClass:[NSNumber class] forKey:@"stickerId"] unsignedIntValue];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
NSUInteger result = 0;
|
||||
result ^= self.contentType.hash;
|
||||
result ^= self.emojiString.hash;
|
||||
result ^= self.stickerId;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (![other isMemberOfClass:self.class]) {
|
||||
return NO;
|
||||
}
|
||||
StickerPackItem *typedOther = (StickerPackItem *)other;
|
||||
if (![NSObject isObject:self.contentType equalToObject:typedOther.contentType]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.emojiString equalToObject:typedOther.emojiString]) {
|
||||
return NO;
|
||||
}
|
||||
if (self.stickerId != typedOther.stickerId) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone
|
||||
{
|
||||
StickerPackItem *result = [[[self class] allocWithZone:zone] init];
|
||||
result->_contentType = self.contentType;
|
||||
result->_emojiString = self.emojiString;
|
||||
result->_stickerId = self.stickerId;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (StickerInfo *)stickerInfoWithStickerPack:(StickerPack *)stickerPack
|
||||
{
|
||||
return [[StickerInfo alloc] initWithPackId:stickerPack.packId packKey:stickerPack.packKey stickerId:self.stickerId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@interface StickerPack ()
|
||||
|
||||
@property (nonatomic) BOOL isInstalled;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation StickerPack
|
||||
|
||||
- (NSUInteger)hash
|
||||
{
|
||||
NSUInteger result = [super hash];
|
||||
result ^= self.author.hash;
|
||||
result ^= self.cover.hash;
|
||||
result ^= self.dateCreated.hash;
|
||||
result ^= self.info.hash;
|
||||
result ^= self.isInstalled;
|
||||
result ^= self.items.hash;
|
||||
result ^= self.title.hash;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)other
|
||||
{
|
||||
if (![super isEqual:other]) {
|
||||
return NO;
|
||||
}
|
||||
StickerPack *typedOther = (StickerPack *)other;
|
||||
if (![NSObject isObject:self.author equalToObject:typedOther.author]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.cover equalToObject:typedOther.cover]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.dateCreated equalToObject:typedOther.dateCreated]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.info equalToObject:typedOther.info]) {
|
||||
return NO;
|
||||
}
|
||||
if (self.isInstalled != typedOther.isInstalled) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.items equalToObject:typedOther.items]) {
|
||||
return NO;
|
||||
}
|
||||
if (![NSObject isObject:self.title equalToObject:typedOther.title]) {
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(nullable NSZone *)zone
|
||||
{
|
||||
StickerPack *result = [self copyAndAssignIdsWithZone:zone];
|
||||
result->_author = self.author;
|
||||
result->_cover = self.cover;
|
||||
result->_dateCreated = self.dateCreated;
|
||||
result->_info = self.info;
|
||||
result->_isInstalled = self.isInstalled;
|
||||
result->_items = self.items;
|
||||
result->_title = self.title;
|
||||
return result;
|
||||
}
|
||||
|
||||
- (instancetype)initWithInfo:(StickerPackInfo *)info
|
||||
title:(nullable NSString *)title
|
||||
author:(nullable NSString *)author
|
||||
cover:(StickerPackItem *)cover
|
||||
stickers:(NSArray<StickerPackItem *> *)items
|
||||
{
|
||||
OWSAssertDebug(info.packId.length > 0);
|
||||
OWSAssertDebug(info.packKey.length > 0);
|
||||
// Title and empty might be nil or empty.
|
||||
OWSAssertDebug(cover);
|
||||
OWSAssertDebug(items.count > 0);
|
||||
|
||||
self = [super initWithUniqueId:[StickerPack uniqueIdForStickerPackInfo:info]];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_info = info;
|
||||
_title = title;
|
||||
_author = author;
|
||||
_cover = cover;
|
||||
_items = items;
|
||||
_dateCreated = [NSDate new];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData *)packId
|
||||
{
|
||||
return self.info.packId;
|
||||
}
|
||||
|
||||
- (NSData *)packKey
|
||||
{
|
||||
return self.info.packKey;
|
||||
}
|
||||
|
||||
- (StickerInfo *)coverInfo
|
||||
{
|
||||
return [[StickerInfo alloc] initWithPackId:self.packId packKey:self.packKey stickerId:self.cover.stickerId];
|
||||
}
|
||||
|
||||
- (NSArray<StickerInfo *> *)stickerInfos
|
||||
{
|
||||
NSMutableArray<StickerInfo *> *stickerInfos = [NSMutableArray new];
|
||||
for (StickerPackItem *item in self.items) {
|
||||
[stickerInfos addObject:[item stickerInfoWithStickerPack:self]];
|
||||
}
|
||||
return stickerInfos;
|
||||
}
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
// This snippet is generated by /Scripts/sds_codegen/sds_generate.py. Do not manually edit it, instead run
|
||||
// `sds_codegen.sh`.
|
||||
|
||||
// clang-format off
|
||||
|
||||
- (instancetype)initWithGrdbId:(int64_t)grdbId
|
||||
uniqueId:(NSString *)uniqueId
|
||||
author:(nullable NSString *)author
|
||||
cover:(StickerPackItem *)cover
|
||||
dateCreated:(NSDate *)dateCreated
|
||||
info:(StickerPackInfo *)info
|
||||
isInstalled:(BOOL)isInstalled
|
||||
items:(NSArray<StickerPackItem *> *)items
|
||||
title:(nullable NSString *)title
|
||||
{
|
||||
self = [super initWithGrdbId:grdbId
|
||||
uniqueId:uniqueId];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
_author = author;
|
||||
_cover = cover;
|
||||
_dateCreated = dateCreated;
|
||||
_info = info;
|
||||
_isInstalled = isInstalled;
|
||||
_items = items;
|
||||
_title = title;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
// --- CODE GENERATION MARKER
|
||||
|
||||
+ (NSString *)uniqueIdForStickerPackInfo:(StickerPackInfo *)info
|
||||
{
|
||||
return info.asKey;
|
||||
}
|
||||
|
||||
- (void)updateWithIsInstalled:(BOOL)isInstalled transaction:(DBWriteTransaction *)transaction
|
||||
{
|
||||
[self anyUpdateWithTransaction:transaction block:^(StickerPack *instance) { instance.isInstalled = isInstalled; }];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
65
SignalServiceKit/Messages/Stickers/StickerPackItem.swift
Normal file
65
SignalServiceKit/Messages/Stickers/StickerPackItem.swift
Normal file
@ -0,0 +1,65 @@
|
||||
//
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc(StickerPackItem)
|
||||
public final class StickerPackItem: NSObject, NSSecureCoding, NSCopying {
|
||||
public let stickerId: UInt32
|
||||
public let emojiString: String
|
||||
public let contentType: String?
|
||||
|
||||
init(
|
||||
stickerId: UInt32,
|
||||
emojiString: String,
|
||||
contentType: String?,
|
||||
) {
|
||||
self.stickerId = stickerId
|
||||
self.emojiString = emojiString
|
||||
self.contentType = contentType?.nilIfEmpty
|
||||
}
|
||||
|
||||
public static var supportsSecureCoding: Bool { true }
|
||||
|
||||
public func encode(with coder: NSCoder) {
|
||||
if let contentType {
|
||||
coder.encode(contentType, forKey: "contentType")
|
||||
}
|
||||
coder.encode(self.emojiString, forKey: "emojiString")
|
||||
coder.encode(NSNumber(value: self.stickerId), forKey: "stickerId")
|
||||
}
|
||||
|
||||
public init?(coder: NSCoder) {
|
||||
self.contentType = (coder.decodeObject(of: NSString.self, forKey: "contentType") as String?)?.nilIfEmpty
|
||||
self.emojiString = coder.decodeObject(of: NSString.self, forKey: "emojiString") as String? ?? ""
|
||||
self.stickerId = coder.decodeObject(of: NSNumber.self, forKey: "stickerId")?.uint32Value ?? 0
|
||||
}
|
||||
|
||||
override public var hash: Int {
|
||||
var hasher = Hasher()
|
||||
hasher.combine(self.contentType)
|
||||
hasher.combine(self.emojiString)
|
||||
hasher.combine(self.stickerId)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let object = object as? Self else { return false }
|
||||
guard self.contentType == object.contentType else { return false }
|
||||
guard self.emojiString == object.emojiString else { return false }
|
||||
guard self.stickerId == object.stickerId else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
return self
|
||||
}
|
||||
|
||||
func stickerInfoWith(stickerPack: StickerPackRecord) -> StickerInfo {
|
||||
let packId = stickerPack.info.packId
|
||||
let packKey = stickerPack.info.packKey
|
||||
return StickerInfo(packId: packId, packKey: packKey, stickerId: self.stickerId)
|
||||
}
|
||||
}
|
||||
163
SignalServiceKit/Messages/Stickers/StickerPackRecord.swift
Normal file
163
SignalServiceKit/Messages/Stickers/StickerPackRecord.swift
Normal file
@ -0,0 +1,163 @@
|
||||
//
|
||||
// Copyright 2026 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
//
|
||||
|
||||
import Foundation
|
||||
public import GRDB
|
||||
|
||||
public final class StickerPackRecord: SDSCodableModel, Decodable, Equatable, NSCopying {
|
||||
public static let databaseTableName: String = "model_StickerPack"
|
||||
public static let recordType: UInt = SDSRecordType.stickerPack.rawValue
|
||||
|
||||
public var id: Int64?
|
||||
public let uniqueId: String
|
||||
public let info: StickerPackInfo
|
||||
public let title: String?
|
||||
public let author: String?
|
||||
public let cover: StickerPackItem
|
||||
public let items: [StickerPackItem]
|
||||
public let dateCreated: Date
|
||||
public private(set) var isInstalled: Bool
|
||||
|
||||
public enum CodingKeys: String, CodingKey, ColumnExpression, CaseIterable {
|
||||
case id
|
||||
case recordType
|
||||
case uniqueId
|
||||
case author
|
||||
case cover
|
||||
case dateCreated
|
||||
case info
|
||||
case isInstalled
|
||||
case items
|
||||
case title
|
||||
}
|
||||
|
||||
public init(from decoder: any Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.id = try container.decodeIfPresent(Int64.self, forKey: .id)
|
||||
self.uniqueId = try container.decode(String.self, forKey: .uniqueId)
|
||||
let infoData = try container.decode(Data.self, forKey: .info)
|
||||
self.info = try LegacySDSSerializer().deserializeLegacySDSData(infoData, ofClass: StickerPackInfo.self)
|
||||
self.title = try container.decodeIfPresent(String.self, forKey: .title)
|
||||
self.author = try container.decodeIfPresent(String.self, forKey: .author)
|
||||
let coverData = try container.decode(Data.self, forKey: .cover)
|
||||
self.cover = try LegacySDSSerializer().deserializeLegacySDSData(coverData, ofClass: StickerPackItem.self)
|
||||
let itemsData = try container.decode(Data.self, forKey: .items)
|
||||
self.items = try LegacySDSSerializer().deserializeLegacyArchivedArray(itemsData, ofClass: StickerPackItem.self)
|
||||
self.dateCreated = Date(timeIntervalSince1970: try container.decode(TimeInterval.self, forKey: .dateCreated))
|
||||
self.isInstalled = try container.decode(Bool.self, forKey: .isInstalled)
|
||||
}
|
||||
|
||||
public func encode(to encoder: any Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encodeIfPresent(self.id, forKey: .id)
|
||||
try container.encode(self.uniqueId, forKey: .uniqueId)
|
||||
try container.encode(Self.recordType, forKey: .recordType)
|
||||
try container.encode(LegacySDSSerializer().serializeAsLegacySDSData(self.info), forKey: .info)
|
||||
try container.encodeIfPresent(self.title, forKey: .title)
|
||||
try container.encodeIfPresent(self.author, forKey: .author)
|
||||
try container.encode(LegacySDSSerializer().serializeAsLegacySDSData(self.cover), forKey: .cover)
|
||||
try container.encode(LegacySDSSerializer().serializeAsLegacySDSData(self.items as [NSSecureCoding] as NSArray), forKey: .items)
|
||||
try container.encode(self.dateCreated.timeIntervalSince1970, forKey: .dateCreated)
|
||||
try container.encode(self.isInstalled, forKey: .isInstalled)
|
||||
}
|
||||
|
||||
public static func ==(lhs: StickerPackRecord, rhs: StickerPackRecord) -> Bool {
|
||||
guard lhs.author == rhs.author else { return false }
|
||||
guard lhs.cover == rhs.cover else { return false }
|
||||
guard lhs.dateCreated == rhs.dateCreated else { return false }
|
||||
guard lhs.info == rhs.info else { return false }
|
||||
guard lhs.isInstalled == rhs.isInstalled else { return false }
|
||||
guard lhs.items == rhs.items else { return false }
|
||||
guard lhs.title == rhs.title else { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
convenience init(
|
||||
info: StickerPackInfo,
|
||||
title: String?,
|
||||
author: String?,
|
||||
cover: StickerPackItem,
|
||||
items: [StickerPackItem],
|
||||
) {
|
||||
owsAssertDebug(!info.packId.isEmpty)
|
||||
owsAssertDebug(!info.packKey.isEmpty)
|
||||
owsAssertDebug(!items.isEmpty)
|
||||
self.init(
|
||||
id: nil,
|
||||
uniqueId: Self.uniqueId(forStickerPackInfo: info),
|
||||
info: info,
|
||||
title: title,
|
||||
author: author,
|
||||
cover: cover,
|
||||
items: items,
|
||||
dateCreated: Date(),
|
||||
isInstalled: false,
|
||||
)
|
||||
}
|
||||
|
||||
private init(
|
||||
id: Int64?,
|
||||
uniqueId: String,
|
||||
info: StickerPackInfo,
|
||||
title: String?,
|
||||
author: String?,
|
||||
cover: StickerPackItem,
|
||||
items: [StickerPackItem],
|
||||
dateCreated: Date,
|
||||
isInstalled: Bool,
|
||||
) {
|
||||
self.id = id
|
||||
self.uniqueId = uniqueId
|
||||
self.info = info
|
||||
self.title = title
|
||||
self.author = author
|
||||
self.cover = cover
|
||||
self.items = items
|
||||
self.dateCreated = dateCreated
|
||||
self.isInstalled = isInstalled
|
||||
}
|
||||
|
||||
public func copy(with zone: NSZone? = nil) -> Any {
|
||||
return Self(
|
||||
id: self.id,
|
||||
uniqueId: self.uniqueId,
|
||||
info: self.info,
|
||||
title: self.title,
|
||||
author: self.author,
|
||||
cover: self.cover,
|
||||
items: self.items,
|
||||
dateCreated: self.dateCreated,
|
||||
isInstalled: self.isInstalled,
|
||||
)
|
||||
}
|
||||
|
||||
public var packId: Data {
|
||||
// This was the effective behavior in Swift via Obj-C.
|
||||
return self.info.packId ?? Data()
|
||||
}
|
||||
|
||||
public var packKey: Data {
|
||||
// This was the effective behavior in Swift via Obj-C.
|
||||
return self.info.packKey ?? Data()
|
||||
}
|
||||
|
||||
public var coverInfo: StickerInfo {
|
||||
let packId = self.info.packId
|
||||
let packKey = self.info.packKey
|
||||
return StickerInfo(packId: packId, packKey: packKey, stickerId: self.cover.stickerId)
|
||||
}
|
||||
|
||||
public func stickerInfos() -> [StickerInfo] {
|
||||
return self.items.map({ $0.stickerInfoWith(stickerPack: self) })
|
||||
}
|
||||
|
||||
static func uniqueId(forStickerPackInfo stickerPackInfo: StickerPackInfo) -> String {
|
||||
return stickerPackInfo.asKey
|
||||
}
|
||||
|
||||
func updateWith(isInstalled: Bool, tx: DBWriteTransaction) {
|
||||
anyUpdate(transaction: tx, block: { $0.isInstalled = isInstalled })
|
||||
}
|
||||
}
|
||||
@ -34,7 +34,6 @@ FOUNDATION_EXPORT const unsigned char SignalServiceKitVersionString[];
|
||||
#import <SignalServiceKit/OWSVerificationStateChangeMessage.h>
|
||||
#import <SignalServiceKit/SSKAccessors+SDS.h>
|
||||
#import <SignalServiceKit/StickerInfo.h>
|
||||
#import <SignalServiceKit/StickerPack.h>
|
||||
#import <SignalServiceKit/TSCall.h>
|
||||
#import <SignalServiceKit/TSContactThread.h>
|
||||
#import <SignalServiceKit/TSErrorMessage.h>
|
||||
|
||||
@ -527,7 +527,7 @@ public enum DatabaseRecovery {
|
||||
PendingViewedReceiptRecord.databaseTableName,
|
||||
// Can be recovered in other ways, after recovery is done.
|
||||
ProfileBadge.databaseTableName,
|
||||
StickerPack.table.tableName,
|
||||
StickerPackRecord.databaseTableName,
|
||||
HiddenRecipient.databaseTableName,
|
||||
// Not essential.
|
||||
StoryContextAssociatedData.databaseTableName,
|
||||
|
||||
@ -40,4 +40,18 @@ struct SDSCodableModelLegacySerializer: SDSSerializer {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func deserializeLegacyArchivedArray<T: NSObject & NSSecureCoding>(_ encodedValue: Data, ofClass cls: T.Type) throws -> [T] {
|
||||
let result: [T]?
|
||||
do {
|
||||
result = try NSKeyedUnarchiver.unarchivedArrayOfObjects(ofClass: cls, from: encodedValue)
|
||||
} catch {
|
||||
Logger.warn("couldn't decode legacy data: \(error)")
|
||||
throw SDSError.invalidValue()
|
||||
}
|
||||
guard let result else {
|
||||
throw SDSError.invalidValue()
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,11 +102,11 @@ public class StickerPackCollectionView: UICollectionView {
|
||||
|
||||
// MARK: Modes
|
||||
|
||||
public func showInstalledPack(stickerPack: StickerPack) {
|
||||
public func showInstalledPack(stickerPack: StickerPackRecord) {
|
||||
stickerPackDataSource = InstalledStickerPackDataSource(stickerPackInfo: stickerPack.info)
|
||||
}
|
||||
|
||||
public func showUninstalledPack(stickerPack: StickerPack) {
|
||||
public func showUninstalledPack(stickerPack: StickerPackRecord) {
|
||||
stickerPackDataSource = TransientStickerPackDataSource(
|
||||
stickerPackInfo: stickerPack.info,
|
||||
shouldDownloadAllStickers: true,
|
||||
@ -117,7 +117,7 @@ public class StickerPackCollectionView: UICollectionView {
|
||||
stickerPackDataSource = RecentStickerPackDataSource()
|
||||
}
|
||||
|
||||
public func showInstalledPackOrRecents(stickerPack: StickerPack?) {
|
||||
public func showInstalledPackOrRecents(stickerPack: StickerPackRecord?) {
|
||||
if let stickerPack {
|
||||
showInstalledPack(stickerPack: stickerPack)
|
||||
} else {
|
||||
@ -225,7 +225,7 @@ public class StickerPackCollectionView: UICollectionView {
|
||||
let installedStickerInfos = stickerPackDataSource.installedStickerInfos
|
||||
|
||||
if stickerPackDataSource is TransientStickerPackDataSource {
|
||||
guard let allStickerInfos = stickerPackDataSource.getStickerPack()?.stickerInfos else {
|
||||
guard let allStickerInfos = stickerPackDataSource.getStickerPack()?.stickerInfos() else {
|
||||
stickerInfos = []
|
||||
owsAssertDebug(installedStickerInfos.isEmpty)
|
||||
return
|
||||
|
||||
@ -22,7 +22,7 @@ public protocol StickerPackDataSource: AnyObject {
|
||||
var title: String? { get }
|
||||
var author: String? { get }
|
||||
|
||||
func getStickerPack() -> StickerPack?
|
||||
func getStickerPack() -> StickerPackRecord?
|
||||
|
||||
var installedCoverInfo: StickerInfo? { get }
|
||||
var installedStickerInfos: [StickerInfo] { get }
|
||||
@ -118,7 +118,7 @@ public class InstalledStickerPackDataSource: BaseStickerPackDataSource {
|
||||
|
||||
private let stickerPackInfo: StickerPackInfo
|
||||
|
||||
fileprivate var stickerPack: StickerPack? {
|
||||
fileprivate var stickerPack: StickerPackRecord? {
|
||||
didSet {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
@ -195,7 +195,7 @@ public class InstalledStickerPackDataSource: BaseStickerPackDataSource {
|
||||
}
|
||||
|
||||
private static func fetchInstalledState(for stickerPackInfo: StickerPackInfo, readTx: DBReadTransaction) -> (
|
||||
stickerPack: StickerPack?,
|
||||
stickerPack: StickerPackRecord?,
|
||||
installedCoverInfo: StickerInfo?,
|
||||
installedStickers: [StickerInfo],
|
||||
) {
|
||||
@ -273,7 +273,7 @@ extension InstalledStickerPackDataSource: StickerPackDataSource {
|
||||
return stickerPack?.author
|
||||
}
|
||||
|
||||
public func getStickerPack() -> StickerPack? {
|
||||
public func getStickerPack() -> StickerPackRecord? {
|
||||
return stickerPack
|
||||
}
|
||||
|
||||
@ -313,7 +313,7 @@ public class TransientStickerPackDataSource: BaseStickerPackDataSource {
|
||||
// If false, only download manifest and cover.
|
||||
private let shouldDownloadAllStickers: Bool
|
||||
|
||||
fileprivate var stickerPack: StickerPack? {
|
||||
fileprivate var stickerPack: StickerPackRecord? {
|
||||
didSet {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
@ -394,7 +394,7 @@ public class TransientStickerPackDataSource: BaseStickerPackDataSource {
|
||||
|
||||
if shouldDownloadAllStickers {
|
||||
var downloadedStickerInfos = [StickerInfo]()
|
||||
for stickerInfo in stickerPack.stickerInfos {
|
||||
for stickerInfo in stickerPack.stickerInfos() {
|
||||
if ensureStickerDownload(stickerPack: stickerPack, stickerInfo: stickerInfo) {
|
||||
downloadedStickerInfos.append(stickerInfo)
|
||||
}
|
||||
@ -448,7 +448,7 @@ public class TransientStickerPackDataSource: BaseStickerPackDataSource {
|
||||
|
||||
// Returns true if sticker is already downloaded.
|
||||
// If not, kicks off the download.
|
||||
private func ensureStickerDownload(stickerPack: StickerPack, stickerInfo: StickerInfo) -> Bool {
|
||||
private func ensureStickerDownload(stickerPack: StickerPackRecord, stickerInfo: StickerInfo) -> Bool {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
guard let stickerPackItem = stickerPack.stickerPackItem(forStickerInfo: stickerInfo) else {
|
||||
@ -552,7 +552,7 @@ extension TransientStickerPackDataSource: StickerPackDataSource {
|
||||
return stickerPack?.author
|
||||
}
|
||||
|
||||
public func getStickerPack() -> StickerPack? {
|
||||
public func getStickerPack() -> StickerPackRecord? {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
if let stickerPack = installedDataSource.getStickerPack() {
|
||||
@ -657,7 +657,7 @@ extension RecentStickerPackDataSource: StickerPackDataSource {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func getStickerPack() -> StickerPack? {
|
||||
public func getStickerPack() -> StickerPackRecord? {
|
||||
owsFailDebug("This method should never be called.")
|
||||
return nil
|
||||
}
|
||||
@ -684,7 +684,7 @@ extension RecentStickerPackDataSource: StickerPackDataSource {
|
||||
|
||||
// MARK: -
|
||||
|
||||
extension StickerPack {
|
||||
extension StickerPackRecord {
|
||||
func stickerPackItem(forStickerInfo stickerInfo: StickerInfo) -> StickerPackItem? {
|
||||
if cover.stickerId == stickerInfo.stickerId {
|
||||
return cover
|
||||
|
||||
@ -377,9 +377,9 @@ private class StickerPickerPageView: UIView {
|
||||
|
||||
private let storyStickerConfiguration: StoryStickerConfiguration
|
||||
|
||||
private var stickerPacks = [StickerPack]()
|
||||
private var stickerPacks = [StickerPackRecord]()
|
||||
|
||||
private var selectedStickerPack: StickerPack? {
|
||||
private var selectedStickerPack: StickerPackRecord? {
|
||||
didSet {
|
||||
selectedPackChanged(oldSelectedPack: oldValue)
|
||||
}
|
||||
@ -534,7 +534,7 @@ private class StickerPickerPageView: UIView {
|
||||
updateSelectedStickerPack(nil)
|
||||
}
|
||||
|
||||
private func updateSelectedStickerPack(_ stickerPack: StickerPack?, scrollToSelected: Bool = false) {
|
||||
private func updateSelectedStickerPack(_ stickerPack: StickerPackRecord?, scrollToSelected: Bool = false) {
|
||||
selectedStickerPack = stickerPack
|
||||
delegate?.updateSelections(scrollToSelectedItem: scrollToSelected)
|
||||
}
|
||||
@ -575,7 +575,7 @@ private class StickerPickerPageView: UIView {
|
||||
return scrollView
|
||||
}()
|
||||
|
||||
private var nextPageStickerPack: StickerPack? {
|
||||
private var nextPageStickerPack: StickerPackRecord? {
|
||||
// If we don't have a pack defined, the first pack is always up next
|
||||
guard let stickerPack = selectedStickerPack else { return stickerPacks.first }
|
||||
|
||||
@ -586,7 +586,7 @@ private class StickerPickerPageView: UIView {
|
||||
return stickerPacks[index + 1]
|
||||
}
|
||||
|
||||
private var previousPageStickerPack: StickerPack? {
|
||||
private var previousPageStickerPack: StickerPackRecord? {
|
||||
// If we don't have a pack defined, the last pack is always previous
|
||||
guard let stickerPack = selectedStickerPack else { return stickerPacks.last }
|
||||
|
||||
@ -692,7 +692,7 @@ private class StickerPickerPageView: UIView {
|
||||
pendingPageChangeUpdates = nil
|
||||
}
|
||||
|
||||
private func selectedPackChanged(oldSelectedPack: StickerPack?) {
|
||||
private func selectedPackChanged(oldSelectedPack: StickerPackRecord?) {
|
||||
AssertIsOnMainThread()
|
||||
|
||||
// We're paging backwards!
|
||||
|
||||
Loading…
Reference in New Issue
Block a user