Merge branch 'database-refactor' into emoji-reacts
This commit is contained in:
commit
f99ae07309
5
Podfile
5
Podfile
|
@ -23,7 +23,7 @@ abstract_target 'GlobalDependencies' do
|
|||
pod 'Reachability'
|
||||
pod 'PureLayout', '~> 3.1.8'
|
||||
pod 'NVActivityIndicatorView'
|
||||
pod 'YYImage', git: 'https://github.com/signalapp/YYImage'
|
||||
pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage'
|
||||
pod 'ZXingObjC'
|
||||
pod 'DifferenceKit'
|
||||
end
|
||||
|
@ -51,7 +51,7 @@ abstract_target 'GlobalDependencies' do
|
|||
pod 'Reachability'
|
||||
pod 'SAMKeychain'
|
||||
pod 'SwiftProtobuf', '~> 1.5.0'
|
||||
pod 'YYImage', git: 'https://github.com/signalapp/YYImage'
|
||||
pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage'
|
||||
pod 'DifferenceKit'
|
||||
end
|
||||
|
||||
|
@ -71,6 +71,7 @@ abstract_target 'GlobalDependencies' do
|
|||
|
||||
target 'SessionUtilitiesKit' do
|
||||
pod 'SAMKeychain'
|
||||
pod 'YYImage/libwebp', git: 'https://github.com/signalapp/YYImage'
|
||||
|
||||
target 'SessionUtilitiesKitTests' do
|
||||
inherit! :complete
|
||||
|
|
20
Podfile.lock
20
Podfile.lock
|
@ -29,6 +29,15 @@ PODS:
|
|||
- DifferenceKit/Core
|
||||
- GRDB.swift/SQLCipher (5.24.1):
|
||||
- SQLCipher (>= 3.4.0)
|
||||
- libwebp (1.2.1):
|
||||
- libwebp/demux (= 1.2.1)
|
||||
- libwebp/mux (= 1.2.1)
|
||||
- libwebp/webp (= 1.2.1)
|
||||
- libwebp/demux (1.2.1):
|
||||
- libwebp/webp
|
||||
- libwebp/mux (1.2.1):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.2.1)
|
||||
- Nimble (10.0.0)
|
||||
- NVActivityIndicatorView (5.1.1):
|
||||
- NVActivityIndicatorView/Base (= 5.1.1)
|
||||
|
@ -121,9 +130,10 @@ PODS:
|
|||
- YapDatabase/SQLCipher/Core
|
||||
- YapDatabase/SQLCipher/Extensions/View (3.1.1):
|
||||
- YapDatabase/SQLCipher/Core
|
||||
- YYImage (1.0.4):
|
||||
- YYImage/Core (= 1.0.4)
|
||||
- YYImage/Core (1.0.4)
|
||||
- YYImage/libwebp (1.0.4):
|
||||
- libwebp
|
||||
- YYImage/Core
|
||||
- ZXingObjC (3.6.5):
|
||||
- ZXingObjC/All (= 3.6.5)
|
||||
- ZXingObjC/All (3.6.5)
|
||||
|
@ -148,7 +158,7 @@ DEPENDENCIES:
|
|||
- SwiftProtobuf (~> 1.5.0)
|
||||
- WebRTC-lib
|
||||
- YapDatabase/SQLCipher (from `https://github.com/oxen-io/session-ios-yap-database.git`, branch `signal-release`)
|
||||
- YYImage (from `https://github.com/signalapp/YYImage`)
|
||||
- YYImage/libwebp (from `https://github.com/signalapp/YYImage`)
|
||||
- ZXingObjC
|
||||
|
||||
SPEC REPOS:
|
||||
|
@ -158,6 +168,7 @@ SPEC REPOS:
|
|||
- CryptoSwift
|
||||
- DifferenceKit
|
||||
- GRDB.swift
|
||||
- libwebp
|
||||
- Nimble
|
||||
- NVActivityIndicatorView
|
||||
- OpenSSL-Universal
|
||||
|
@ -212,6 +223,7 @@ SPEC CHECKSUMS:
|
|||
Curve25519Kit: e63f9859ede02438ae3defc5e1a87e09d1ec7ee6
|
||||
DifferenceKit: 5659c430bb7fe45876fa32ce5cba5d6167f0c805
|
||||
GRDB.swift: b3180ce2135fc06a453297889b746b1478c4d8c7
|
||||
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
|
||||
Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84
|
||||
NVActivityIndicatorView: 1f6c5687f1171810aa27a3296814dc2d7dec3667
|
||||
OpenSSL-Universal: e7311447fd2419f57420c79524b641537387eff2
|
||||
|
@ -230,6 +242,6 @@ SPEC CHECKSUMS:
|
|||
YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: 6ab902a81a379cc2c0a9a92c334c78d413190338
|
||||
PODFILE CHECKSUM: f0857369c4831b2e5c1946345e76e493f3286805
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
|
|
@ -817,6 +817,7 @@
|
|||
FDF2220B2818F38D000A4995 /* SessionApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF2220A2818F38D000A4995 /* SessionApp.swift */; };
|
||||
FDF2220F281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF2220E281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift */; };
|
||||
FDF22211281B5E0B000A4995 /* TableRecord+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF22210281B5E0B000A4995 /* TableRecord+Utilities.swift */; };
|
||||
FDF40CDE2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF40CDD2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift */; };
|
||||
FDFD645927F26C6800808CA1 /* Array+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Utilities.swift */; };
|
||||
FDFD645B27F26D4600808CA1 /* Data+Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */; };
|
||||
FDFD645D27F273F300808CA1 /* MockGeneralCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */; };
|
||||
|
@ -1871,6 +1872,7 @@
|
|||
FDF2220A2818F38D000A4995 /* SessionApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionApp.swift; sourceTree = "<group>"; };
|
||||
FDF2220E281B55E6000A4995 /* QueryInterfaceRequest+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueryInterfaceRequest+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDF22210281B5E0B000A4995 /* TableRecord+Utilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TableRecord+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDF40CDD2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _004_RemoveLegacyYDB.swift; sourceTree = "<group>"; };
|
||||
FDFD645A27F26D4600808CA1 /* Data+Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Utilities.swift"; sourceTree = "<group>"; };
|
||||
FDFD645C27F273F300808CA1 /* MockGeneralCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGeneralCache.swift; sourceTree = "<group>"; };
|
||||
FDFDE123282D04F20098B17F /* MediaDismissAnimationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaDismissAnimationController.swift; sourceTree = "<group>"; };
|
||||
|
@ -3517,6 +3519,7 @@
|
|||
FDF0B7412804EA4F004C14C5 /* _002_SetupStandardJobs.swift */,
|
||||
FD17D79827F40AB800122BE0 /* _003_YDBToGRDBMigration.swift */,
|
||||
FD09B7E4288670BB00ED0B66 /* _005_EmojiReacts.swift */,
|
||||
FDF40CDD2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift */,
|
||||
);
|
||||
path = Migrations;
|
||||
sourceTree = "<group>";
|
||||
|
@ -5150,6 +5153,7 @@
|
|||
FD09799927FFC1A300936362 /* Attachment.swift in Sources */,
|
||||
FD245C5F2850662200B966DD /* OWSWindowManager.m in Sources */,
|
||||
C3471ECB2555356A00297E91 /* MessageSender+Encryption.swift in Sources */,
|
||||
FDF40CDE2897A1BC006A0CC4 /* _004_RemoveLegacyYDB.swift in Sources */,
|
||||
FDF0B74928060D13004C14C5 /* QuotedReplyModel.swift in Sources */,
|
||||
FD77289A284AF1BD0018502F /* Sodium+Utilities.swift in Sources */,
|
||||
FD5C7309285007920029977D /* BlindedIdLookup.swift in Sources */,
|
||||
|
@ -6893,7 +6897,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 360;
|
||||
CURRENT_PROJECT_VERSION = 361;
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -6965,7 +6969,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Session/Meta/Signal.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 360;
|
||||
CURRENT_PROJECT_VERSION = 361;
|
||||
DEVELOPMENT_TEAM = SUQ8J2PCT7;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
|
|
@ -129,7 +129,10 @@ public class MediaView: UIView {
|
|||
configure(forError: .failed)
|
||||
return false
|
||||
}
|
||||
guard attachment.state != .uploaded else { return false }
|
||||
|
||||
// If this message was uploaded on a different device it'll now be seen as 'downloaded' (but
|
||||
// will still be outgoing - we don't want to show a loading indicator in this case)
|
||||
guard attachment.state != .uploaded && attachment.state != .downloaded else { return false }
|
||||
|
||||
let loader = MediaLoaderView()
|
||||
addSubview(loader)
|
||||
|
@ -164,9 +167,13 @@ public class MediaView: UIView {
|
|||
}
|
||||
strongSelf.tryToLoadMedia(
|
||||
loadMediaBlock: { applyMediaBlock in
|
||||
guard attachment.isValid else { return }
|
||||
guard attachment.isValid else {
|
||||
self?.configure(forError: .invalid)
|
||||
return
|
||||
}
|
||||
guard let filePath: String = attachment.originalFilePath else {
|
||||
owsFailDebug("Attachment stream missing original file path.")
|
||||
self?.configure(forError: .invalid)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -177,6 +184,7 @@ public class MediaView: UIView {
|
|||
|
||||
guard let image: YYImage = media as? YYImage else {
|
||||
owsFailDebug("Media has unexpected type: \(type(of: media))")
|
||||
self?.configure(forError: .invalid)
|
||||
return
|
||||
}
|
||||
// FIXME: Animated images flicker when reloading the cells (even though they are in the cache)
|
||||
|
@ -216,12 +224,18 @@ public class MediaView: UIView {
|
|||
}
|
||||
self?.tryToLoadMedia(
|
||||
loadMediaBlock: { applyMediaBlock in
|
||||
guard attachment.isValid else { return }
|
||||
guard attachment.isValid else {
|
||||
self?.configure(forError: .invalid)
|
||||
return
|
||||
}
|
||||
|
||||
attachment.thumbnail(
|
||||
size: .large,
|
||||
success: { image, _ in applyMediaBlock(image) },
|
||||
failure: { Logger.error("Could not load thumbnail") }
|
||||
failure: {
|
||||
Logger.error("Could not load thumbnail")
|
||||
self?.configure(forError: .invalid)
|
||||
}
|
||||
)
|
||||
},
|
||||
applyMediaBlock: { media in
|
||||
|
@ -229,6 +243,7 @@ public class MediaView: UIView {
|
|||
|
||||
guard let image: UIImage = media as? UIImage else {
|
||||
owsFailDebug("Media has unexpected type: \(type(of: media))")
|
||||
self?.configure(forError: .invalid)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -277,12 +292,18 @@ public class MediaView: UIView {
|
|||
}
|
||||
self?.tryToLoadMedia(
|
||||
loadMediaBlock: { applyMediaBlock in
|
||||
guard attachment.isValid else { return }
|
||||
guard attachment.isValid else {
|
||||
self?.configure(forError: .invalid)
|
||||
return
|
||||
}
|
||||
|
||||
attachment.thumbnail(
|
||||
size: .medium,
|
||||
success: { image, _ in applyMediaBlock(image) },
|
||||
failure: { Logger.error("Could not load thumbnail") }
|
||||
failure: {
|
||||
Logger.error("Could not load thumbnail")
|
||||
self?.configure(forError: .invalid)
|
||||
}
|
||||
)
|
||||
},
|
||||
applyMediaBlock: { media in
|
||||
|
@ -290,6 +311,7 @@ public class MediaView: UIView {
|
|||
|
||||
guard let image: UIImage = media as? UIImage else {
|
||||
owsFailDebug("Media has unexpected type: \(type(of: media))")
|
||||
self?.configure(forError: .invalid)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,15 @@ class MediaDetailViewController: OWSViewController, UIScrollViewDelegate, OWSVid
|
|||
// MARK: - Functions
|
||||
|
||||
private func updateMinZoomScale() {
|
||||
guard let image: UIImage = image else {
|
||||
let maybeImageSize: CGSize? = {
|
||||
switch self.mediaView {
|
||||
case let imageView as UIImageView: return (imageView.image?.size ?? .zero)
|
||||
case let imageView as YYAnimatedImageView: return (imageView.image?.size ?? .zero)
|
||||
default: return nil
|
||||
}
|
||||
}()
|
||||
|
||||
guard let imageSize: CGSize = maybeImageSize else {
|
||||
self.scrollView.minimumZoomScale = 1
|
||||
self.scrollView.maximumZoomScale = 1
|
||||
self.scrollView.zoomScale = 1
|
||||
|
@ -138,13 +146,13 @@ class MediaDetailViewController: OWSViewController, UIScrollViewDelegate, OWSVid
|
|||
|
||||
let viewSize: CGSize = self.scrollView.bounds.size
|
||||
|
||||
guard image.size.width > 0 && image.size.height > 0 else {
|
||||
SNLog("Invalid image dimensions (\(image.size.width), \(image.size.height))")
|
||||
return;
|
||||
guard imageSize.width > 0 && imageSize.height > 0 else {
|
||||
SNLog("Invalid image dimensions (\(imageSize.width), \(imageSize.height))")
|
||||
return
|
||||
}
|
||||
|
||||
let scaleWidth: CGFloat = (viewSize.width / image.size.width)
|
||||
let scaleHeight: CGFloat = (viewSize.height / image.size.height)
|
||||
let scaleWidth: CGFloat = (viewSize.width / imageSize.width)
|
||||
let scaleHeight: CGFloat = (viewSize.height / imageSize.height)
|
||||
let minScale: CGFloat = min(scaleWidth, scaleHeight)
|
||||
|
||||
if minScale != self.scrollView.minimumZoomScale {
|
||||
|
|
|
@ -240,54 +240,40 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
private func showFailedMigrationAlert(error: Error?) {
|
||||
let alert = UIAlertController(
|
||||
title: "Session",
|
||||
message: ((error as? StorageError) == StorageError.devRemigrationRequired ?
|
||||
"The database has changed since the last version and you need to re-migrate (this will close the app and migrate on the next launch)" :
|
||||
"DATABASE_MIGRATION_FAILED".localized()
|
||||
),
|
||||
message: "DATABASE_MIGRATION_FAILED".localized(),
|
||||
preferredStyle: .alert
|
||||
)
|
||||
|
||||
switch (error as? StorageError) {
|
||||
case .devRemigrationRequired:
|
||||
alert.addAction(UIAlertAction(title: "Re-Migrate Database", style: .default) { _ in
|
||||
Storage.deleteDatabaseFiles()
|
||||
try? Storage.deleteDbKeys()
|
||||
exit(1)
|
||||
})
|
||||
|
||||
default:
|
||||
alert.addAction(UIAlertAction(title: "modal_share_logs_title".localized(), style: .default) { _ in
|
||||
ShareLogsModal.shareLogs(from: alert) { [weak self] in
|
||||
self?.showFailedMigrationAlert(error: error)
|
||||
}
|
||||
})
|
||||
alert.addAction(UIAlertAction(title: "vc_restore_title".localized(), style: .destructive) { _ in
|
||||
// Remove the legacy database and any message hashes that have been migrated to the new DB
|
||||
try? SUKLegacy.deleteLegacyDatabaseFilesAndKey()
|
||||
|
||||
Storage.shared.write { db in
|
||||
try SnodeReceivedMessageInfo.deleteAll(db)
|
||||
}
|
||||
|
||||
// The re-run the migration (should succeed since there is no data)
|
||||
AppSetup.runPostSetupMigrations(
|
||||
migrationProgressChanged: { [weak self] progress, minEstimatedTotalTime in
|
||||
self?.loadingViewController?.updateProgress(
|
||||
progress: progress,
|
||||
minEstimatedTotalTime: minEstimatedTotalTime
|
||||
)
|
||||
},
|
||||
migrationsCompletion: { [weak self] error, needsConfigSync in
|
||||
guard error == nil else {
|
||||
self?.showFailedMigrationAlert(error: error)
|
||||
return
|
||||
}
|
||||
|
||||
self?.completePostMigrationSetup(needsConfigSync: needsConfigSync)
|
||||
}
|
||||
alert.addAction(UIAlertAction(title: "modal_share_logs_title".localized(), style: .default) { _ in
|
||||
ShareLogsModal.shareLogs(from: alert) { [weak self] in
|
||||
self?.showFailedMigrationAlert(error: error)
|
||||
}
|
||||
})
|
||||
alert.addAction(UIAlertAction(title: "vc_restore_title".localized(), style: .destructive) { _ in
|
||||
// Remove the legacy database and any message hashes that have been migrated to the new DB
|
||||
try? SUKLegacy.deleteLegacyDatabaseFilesAndKey()
|
||||
|
||||
Storage.shared.write { db in
|
||||
try SnodeReceivedMessageInfo.deleteAll(db)
|
||||
}
|
||||
|
||||
// The re-run the migration (should succeed since there is no data)
|
||||
AppSetup.runPostSetupMigrations(
|
||||
migrationProgressChanged: { [weak self] progress, minEstimatedTotalTime in
|
||||
self?.loadingViewController?.updateProgress(
|
||||
progress: progress,
|
||||
minEstimatedTotalTime: minEstimatedTotalTime
|
||||
)
|
||||
})
|
||||
}
|
||||
},
|
||||
migrationsCompletion: { [weak self] error, needsConfigSync in
|
||||
guard error == nil else {
|
||||
self?.showFailedMigrationAlert(error: error)
|
||||
return
|
||||
}
|
||||
|
||||
self?.completePostMigrationSetup(needsConfigSync: needsConfigSync)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
alert.addAction(UIAlertAction(title: "Close", style: .default) { _ in
|
||||
DDLog.flushLog()
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -652,7 +652,7 @@
|
|||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
|
||||
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
|
||||
"LOADING_CONVERSATIONS" = "Loading Conversations...";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore your device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
|
||||
"CHATS_TITLE" = "Chats";
|
||||
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
|
||||
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import NVActivityIndicatorView
|
||||
import UIKit
|
||||
import SessionMessagingKit
|
||||
|
||||
final class PathVC : BaseVC {
|
||||
|
||||
|
|
|
@ -298,8 +298,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
|||
return button
|
||||
}
|
||||
|
||||
let debugReMigrateButton = getSettingButton(withTitle: "DEBUG - Re-Migrate Database", color: Colors.destructive, action: #selector(remigrateDatabase))
|
||||
|
||||
let pathButton = getSettingButton(withTitle: NSLocalizedString("vc_path_title", comment: ""), color: Colors.text, action: #selector(showPath))
|
||||
let pathStatusView = PathStatusView()
|
||||
pathStatusView.set(.width, to: PathStatusView.size)
|
||||
|
@ -310,8 +308,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
|||
pathStatusView.autoVCenterInSuperview()
|
||||
|
||||
return [
|
||||
getSeparator(),
|
||||
debugReMigrateButton,
|
||||
getSeparator(),
|
||||
pathButton,
|
||||
getSeparator(),
|
||||
|
@ -603,22 +599,6 @@ final class SettingsVC: BaseVC, AvatarViewHelperDelegate {
|
|||
navigationController!.present(shareVC, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@objc private func remigrateDatabase() {
|
||||
let alert = UIAlertController(
|
||||
title: "Session",
|
||||
message: "Are you sure you want to re-migrate from your old database state?\n\nWarning: If you had a migration error and picked the \"Restore your account\" option this will result in a complete loss of data and the need to manually restore from the seed",
|
||||
preferredStyle: .alert
|
||||
)
|
||||
alert.addAction(UIAlertAction(title: "Re-migrate", style: .destructive) { _ in
|
||||
Storage.deleteDatabaseFiles()
|
||||
try? Storage.deleteDbKeys()
|
||||
exit(1)
|
||||
})
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .default))
|
||||
|
||||
navigationController?.present(alert, animated: true)
|
||||
}
|
||||
|
||||
@objc private func showPath() {
|
||||
let pathVC = PathVC()
|
||||
navigationController!.pushViewController(pathVC, animated: true)
|
||||
|
|
|
@ -13,7 +13,9 @@ public enum SNMessagingKit { // Just to make the external API nice
|
|||
[
|
||||
_003_YDBToGRDBMigration.self
|
||||
],
|
||||
[],
|
||||
[
|
||||
_004_RemoveLegacyYDB.self
|
||||
],
|
||||
[
|
||||
_005_EmojiReacts.self
|
||||
]
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
|
||||
|
||||
import Foundation
|
||||
import GRDB
|
||||
import Curve25519Kit
|
||||
import SessionUtilitiesKit
|
||||
import SessionSnodeKit
|
||||
|
||||
/// This migration removes the legacy YapDatabase files
|
||||
enum _004_RemoveLegacyYDB: Migration {
|
||||
static let target: TargetMigrations.Identifier = .messagingKit
|
||||
static let identifier: String = "RemoveLegacyYDB"
|
||||
static let needsConfigSync: Bool = false
|
||||
static let minExpectedRunDuration: TimeInterval = 0.1
|
||||
|
||||
static func migrate(_ db: Database) throws {
|
||||
try? SUKLegacy.deleteLegacyDatabaseFilesAndKey()
|
||||
Storage.update(progress: 1, for: self, in: target) // In case this is the last migration
|
||||
}
|
||||
}
|
|
@ -339,6 +339,23 @@ extension Attachment {
|
|||
default: return (self.isValid, self.duration)
|
||||
}
|
||||
}()
|
||||
// Regenerate this just in case we added support since the attachment was inserted into
|
||||
// the database (eg. manually downloaded in a later update)
|
||||
let isVisualMedia: Bool = (
|
||||
MIMETypeUtil.isImage(contentType) ||
|
||||
MIMETypeUtil.isVideo(contentType) ||
|
||||
MIMETypeUtil.isAnimated(contentType)
|
||||
)
|
||||
let attachmentResolution: CGSize? = {
|
||||
if let width: UInt = self.width, let height: UInt = self.height, width > 0, height > 0 {
|
||||
return CGSize(width: Int(width), height: Int(height))
|
||||
}
|
||||
guard isVisualMedia else { return nil }
|
||||
guard state == .downloaded else { return nil }
|
||||
guard let originalFilePath: String = originalFilePath else { return nil }
|
||||
|
||||
return Attachment.imageSize(contentType: contentType, originalFilePath: originalFilePath)
|
||||
}()
|
||||
|
||||
return Attachment(
|
||||
id: self.id,
|
||||
|
@ -351,10 +368,16 @@ extension Attachment {
|
|||
sourceFilename: sourceFilename,
|
||||
downloadUrl: (downloadUrl ?? self.downloadUrl),
|
||||
localRelativeFilePath: (localRelativeFilePath ?? self.localRelativeFilePath),
|
||||
width: width,
|
||||
height: height,
|
||||
width: attachmentResolution.map { UInt($0.width) },
|
||||
height: attachmentResolution.map { UInt($0.height) },
|
||||
duration: duration,
|
||||
isVisualMedia: isVisualMedia,
|
||||
isVisualMedia: (
|
||||
// Regenerate this just in case we added support since the attachment was inserted into
|
||||
// the database (eg. manually downloaded in a later update)
|
||||
MIMETypeUtil.isImage(contentType) ||
|
||||
MIMETypeUtil.isVideo(contentType) ||
|
||||
MIMETypeUtil.isAnimated(contentType)
|
||||
),
|
||||
isValid: isValid,
|
||||
encryptionKey: (encryptionKey ?? self.encryptionKey),
|
||||
digest: (digest ?? self.digest),
|
||||
|
|
|
@ -72,6 +72,7 @@ public extension BlindedIdLookup {
|
|||
static func fetchOrCreate(
|
||||
_ db: Database,
|
||||
blindedId: String,
|
||||
sessionId: String? = nil,
|
||||
openGroupServer: String,
|
||||
openGroupPublicKey: String,
|
||||
isCheckingForOutbox: Bool,
|
||||
|
@ -90,6 +91,22 @@ public extension BlindedIdLookup {
|
|||
// If the lookup already has a resolved sessionId then just return it immediately
|
||||
guard lookup.sessionId == nil else { return lookup }
|
||||
|
||||
// If we we given a sessionId then validate it is correct and if so save it
|
||||
if
|
||||
let sessionId: String = sessionId,
|
||||
dependencies.sodium.sessionId(
|
||||
sessionId,
|
||||
matchesBlindedId: blindedId,
|
||||
serverPublicKey: openGroupPublicKey,
|
||||
genericHash: dependencies.genericHash
|
||||
)
|
||||
{
|
||||
lookup = try lookup
|
||||
.with(sessionId: sessionId)
|
||||
.saved(db)
|
||||
return lookup
|
||||
}
|
||||
|
||||
// We now need to try to match the blinded id to an existing contact, this can only be done by looping
|
||||
// through all approved contacts and generating a blinded id for the provided open group for each to
|
||||
// see if it matches the provided blindedId
|
||||
|
|
|
@ -14,7 +14,7 @@ public final class FileServerAPI: NSObject {
|
|||
public static let oldServerPublicKey = "7cb31905b55cd5580c686911debf672577b3fb0bff81df4ce2d5c4cb3a7aaa69"
|
||||
@objc public static let server = "http://filev2.getsession.org"
|
||||
public static let serverPublicKey = "da21e1d886c6fbaea313f75298bd64aab03a97ce985b46bb2dad9f2089c8ee59"
|
||||
public static let maxFileSize = 10_000_000 // 10 MB
|
||||
public static let maxFileSize = (10 * 1024 * 1024) // 10 MB
|
||||
/// The file server has a file size limit of `maxFileSize`, which the Service Nodes try to enforce as well. However, the limit applied by the Service Nodes
|
||||
/// is on the **HTTP request** and not the actual file size. Because the file server expects the file data to be base 64 encoded, the size of the HTTP
|
||||
/// request for a given file will be at least `ceil(n / 3) * 4` bytes, where n is the file size in bytes. This is the minimum size because there might also
|
||||
|
|
|
@ -372,6 +372,8 @@ public enum GarbageCollectionJob: JobExecutor {
|
|||
}
|
||||
catch { deletionErrors.append(error) }
|
||||
}
|
||||
|
||||
SNLog("[GarbageCollectionJob] Removed \(orphanedAttachmentFiles.count) orphaned attachment\(orphanedAttachmentFiles.count == 1 ? "" : "s")")
|
||||
}
|
||||
|
||||
// Orphaned profile avatar files (actual deletion)
|
||||
|
@ -393,6 +395,8 @@ public enum GarbageCollectionJob: JobExecutor {
|
|||
}
|
||||
catch { deletionErrors.append(error) }
|
||||
}
|
||||
|
||||
SNLog("[GarbageCollectionJob] Removed \(orphanedAvatarFiles.count) orphaned avatar image\(orphanedAvatarFiles.count == 1 ? "" : "s")")
|
||||
}
|
||||
|
||||
// Report a single file deletion as a job failure (even if other content was successfully removed)
|
||||
|
|
|
@ -617,28 +617,37 @@ public final class OpenGroupManager: NSObject {
|
|||
dependencies: dependencies
|
||||
)
|
||||
|
||||
// If the message was an outgoing message then attempt to unblind the recipient (this will help put
|
||||
// messages in the correct thread in case of message request approval race conditions as well as
|
||||
// during device sync'ing and restoration)
|
||||
// We want to update the BlindedIdLookup cache with the message info so we can avoid using the
|
||||
// "expensive" lookup when possible
|
||||
let lookup: BlindedIdLookup = try {
|
||||
// Minor optimisation to avoid processing the same sender multiple times in the same
|
||||
// 'handleMessages' call (since the 'mapping' call is done within a transaction we
|
||||
// will never have a mapping come through part-way through processing these messages)
|
||||
if let result: BlindedIdLookup = lookupCache[message.recipient] {
|
||||
return result
|
||||
}
|
||||
|
||||
return try BlindedIdLookup.fetchOrCreate(
|
||||
db,
|
||||
blindedId: (fromOutbox ?
|
||||
message.recipient :
|
||||
message.sender
|
||||
),
|
||||
sessionId: (fromOutbox ?
|
||||
nil :
|
||||
processedMessage?.threadId
|
||||
),
|
||||
openGroupServer: server.lowercased(),
|
||||
openGroupPublicKey: openGroup.publicKey,
|
||||
isCheckingForOutbox: fromOutbox,
|
||||
dependencies: dependencies
|
||||
)
|
||||
}()
|
||||
lookupCache[message.recipient] = lookup
|
||||
|
||||
// We also need to set the 'syncTarget' for outgoing messages to be consistent with
|
||||
// standard messages
|
||||
if fromOutbox {
|
||||
// Attempt to un-blind the 'message.recipient'
|
||||
let lookup: BlindedIdLookup = try {
|
||||
// Minor optimisation to avoid processing the same sender multiple times in the same
|
||||
// 'handleMessages' call (since the 'mapping' call is done within a transaction we
|
||||
// will never have a mapping come through part-way through processing these messages)
|
||||
if let result: BlindedIdLookup = lookupCache[message.recipient] {
|
||||
return result
|
||||
}
|
||||
|
||||
return try BlindedIdLookup.fetchOrCreate(
|
||||
db,
|
||||
blindedId: message.recipient,
|
||||
openGroupServer: server.lowercased(),
|
||||
openGroupPublicKey: openGroup.publicKey,
|
||||
isCheckingForOutbox: true,
|
||||
dependencies: dependencies
|
||||
)
|
||||
}()
|
||||
let syncTarget: String = (lookup.sessionId ?? message.recipient)
|
||||
|
||||
switch processedMessage?.messageInfo.variant {
|
||||
|
@ -650,8 +659,6 @@ public final class OpenGroupManager: NSObject {
|
|||
|
||||
default: break
|
||||
}
|
||||
|
||||
lookupCache[message.recipient] = lookup
|
||||
}
|
||||
|
||||
if let messageInfo: MessageReceiveJob.Details.MessageInfo = processedMessage?.messageInfo {
|
||||
|
|
|
@ -128,8 +128,8 @@ public final class PushNotificationAPI : NSObject {
|
|||
promises.append(
|
||||
attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global()) {
|
||||
OnionRequestAPI.sendOnionRequest(request, to: server, using: .v2, with: serverPublicKey)
|
||||
.map2 { _, response -> Void in
|
||||
guard let response: UpdateRegistrationResponse = try? response?.decoded(as: UpdateRegistrationResponse.self) else {
|
||||
.map2 { _, data -> Void in
|
||||
guard let response: UpdateRegistrationResponse = try? data?.decoded(as: UpdateRegistrationResponse.self) else {
|
||||
return SNLog("Couldn't register device token.")
|
||||
}
|
||||
guard response.body.code != 0 else {
|
||||
|
|
|
@ -186,30 +186,7 @@ public final class Storage {
|
|||
SNLog("[Migration Error] Migration failed with error: \(error)")
|
||||
}
|
||||
|
||||
// TODO: Remove this once everyone has updated
|
||||
var finalError: Error? = error
|
||||
let jobTableInfo: [Row] = (try? Row.fetchAll(db, sql: "PRAGMA table_info(\(Job.databaseTableName))"))
|
||||
.defaulting(to: [])
|
||||
if !jobTableInfo.contains(where: { $0["name"] == "shouldSkipLaunchBecomeActive" }) {
|
||||
finalError = StorageError.devRemigrationRequired
|
||||
}
|
||||
// Forcibly change any 'infoUpdates' on open groups from '-1' to '0' (-1 is invalid)
|
||||
try? db.execute(literal: """
|
||||
UPDATE openGroup
|
||||
SET infoUpdates = 0
|
||||
WHERE openGroup.infoUpdates = -1
|
||||
""")
|
||||
// TODO: Remove this once everyone has updated
|
||||
let openGroupTableInfo: [Row] = (try? Row.fetchAll(db, sql: "PRAGMA table_info(openGroup)"))
|
||||
.defaulting(to: [])
|
||||
if !openGroupTableInfo.contains(where: { $0["name"] == "pollFailureCount" }) {
|
||||
try? db.execute(literal: """
|
||||
ALTER TABLE openGroup
|
||||
ADD pollFailureCount INTEGER NOT NULL DEFAULT 0
|
||||
""")
|
||||
}
|
||||
|
||||
onComplete(finalError, needsConfigSync)
|
||||
onComplete(error, needsConfigSync)
|
||||
}
|
||||
|
||||
// Note: The non-async migration should only be used for unit tests
|
||||
|
@ -314,14 +291,13 @@ public final class Storage {
|
|||
try? self.deleteDbKeys()
|
||||
}
|
||||
|
||||
// TODO: Change these back to private
|
||||
public/*private*/ static func deleteDatabaseFiles() {
|
||||
private static func deleteDatabaseFiles() {
|
||||
OWSFileSystem.deleteFile(databasePath)
|
||||
OWSFileSystem.deleteFile(databasePathShm)
|
||||
OWSFileSystem.deleteFile(databasePathWal)
|
||||
}
|
||||
|
||||
public/*private*/ static func deleteDbKeys() throws {
|
||||
private static func deleteDbKeys() throws {
|
||||
try SSKDefaultKeychainStorage.shared.remove(service: keychainService, key: dbCipherKeySpecKey)
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,9 @@ extern NSString *const OWSMimeTypeImageTiff1;
|
|||
extern NSString *const OWSMimeTypeImageTiff2;
|
||||
extern NSString *const OWSMimeTypeImageBmp1;
|
||||
extern NSString *const OWSMimeTypeImageBmp2;
|
||||
extern NSString *const OWSMimeTypeImageWebp;
|
||||
extern NSString *const OWSMimeTypeImageHeic;
|
||||
extern NSString *const OWSMimeTypeImageHeif;
|
||||
extern NSString *const OWSMimeTypeUnknownForTests;
|
||||
|
||||
extern NSString *const kOversizeTextAttachmentUTI;
|
||||
|
@ -36,6 +39,10 @@ extern NSString *const kSyncMessageFileExtension;
|
|||
+ (nullable NSString *)getSupportedExtensionFromImageMIMEType:(NSString *)supportedMIMEType;
|
||||
+ (nullable NSString *)getSupportedExtensionFromAnimatedMIMEType:(NSString *)supportedMIMEType;
|
||||
|
||||
+ (NSArray<NSString *> *)supportedImageMIMETypes;
|
||||
+ (NSArray<NSString *> *)supportedAnimatedImageMIMETypes;
|
||||
+ (NSArray<NSString *> *)supportedVideoMIMETypes;
|
||||
|
||||
+ (BOOL)isAnimated:(NSString *)contentType;
|
||||
+ (BOOL)isImage:(NSString *)contentType;
|
||||
+ (BOOL)isVideo:(NSString *)contentType;
|
||||
|
|
|
@ -19,6 +19,9 @@ NSString *const OWSMimeTypeImageTiff1 = @"image/tiff";
|
|||
NSString *const OWSMimeTypeImageTiff2 = @"image/x-tiff";
|
||||
NSString *const OWSMimeTypeImageBmp1 = @"image/bmp";
|
||||
NSString *const OWSMimeTypeImageBmp2 = @"image/x-windows-bmp";
|
||||
NSString *const OWSMimeTypeImageWebp = @"image/webp";
|
||||
NSString *const OWSMimeTypeImageHeic = @"image/heic";
|
||||
NSString *const OWSMimeTypeImageHeif = @"image/heif";
|
||||
NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype";
|
||||
NSString *const OWSMimeTypeApplicationZip = @"application/zip";
|
||||
NSString *const OWSMimeTypeApplicationPdf = @"application/pdf";
|
||||
|
@ -85,7 +88,8 @@ NSString *const kSyncMessageFileExtension = @"bin";
|
|||
@"image/bmp" : @"bmp",
|
||||
@"image/x-windows-bmp" : @"bmp",
|
||||
@"image/gif" : @"gif",
|
||||
@"image/x-icon": @"ico"
|
||||
@"image/x-icon": @"ico",
|
||||
OWSMimeTypeImageWebp : @"webp"
|
||||
};
|
||||
});
|
||||
return result;
|
||||
|
@ -97,6 +101,7 @@ NSString *const kSyncMessageFileExtension = @"bin";
|
|||
dispatch_once(&onceToken, ^{
|
||||
result = @{
|
||||
OWSMimeTypeImageGif : @"gif",
|
||||
OWSMimeTypeImageWebp : @"image/webp",
|
||||
};
|
||||
});
|
||||
return result;
|
||||
|
@ -175,7 +180,8 @@ NSString *const kSyncMessageFileExtension = @"bin";
|
|||
@"jpeg" : @"image/jpeg",
|
||||
@"jpg" : @"image/jpeg",
|
||||
@"tif" : @"image/tiff",
|
||||
@"tiff" : @"image/tiff"
|
||||
@"tiff" : @"image/tiff",
|
||||
@"webp" : OWSMimeTypeImageWebp
|
||||
};
|
||||
});
|
||||
return result;
|
||||
|
@ -187,6 +193,7 @@ NSString *const kSyncMessageFileExtension = @"bin";
|
|||
dispatch_once(&onceToken, ^{
|
||||
result = @{
|
||||
@"gif" : OWSMimeTypeImageGif,
|
||||
@"image/webp" : OWSMimeTypeImageWebp
|
||||
};
|
||||
});
|
||||
return result;
|
||||
|
@ -556,6 +563,36 @@ NSString *const kSyncMessageFileExtension = @"bin";
|
|||
return result;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)supportedImageMIMETypes
|
||||
{
|
||||
static NSArray<NSString *> *result = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
result = [self supportedImageMIMETypesToExtensionTypes].allKeys;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)supportedAnimatedImageMIMETypes
|
||||
{
|
||||
static NSArray<NSString *> *result = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
result = [self supportedAnimatedMIMETypesToExtensionTypes].allKeys;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)supportedVideoMIMETypes
|
||||
{
|
||||
static NSArray<NSString *> *result = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
result = [self supportedVideoMIMETypesToExtensionTypes].allKeys;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSDictionary *)genericMIMETypesToExtensionTypes
|
||||
{
|
||||
static NSDictionary *result = nil;
|
||||
|
@ -1386,6 +1423,8 @@ NSString *const kSyncMessageFileExtension = @"bin";
|
|||
@"image/fif" : @"fif",
|
||||
@"image/g3fax" : @"g3",
|
||||
@"image/gif" : @"gif",
|
||||
@"image/heic" : @"heic",
|
||||
@"image/heif" : @"heif",
|
||||
@"image/ief" : @"ief",
|
||||
@"image/jpeg" : @"jpg",
|
||||
@"image/jutvision" : @"jut",
|
||||
|
@ -1935,6 +1974,8 @@ NSString *const kSyncMessageFileExtension = @"bin";
|
|||
@"hal" : @"application/vnd.hal+xml",
|
||||
@"hbci" : @"application/vnd.hbci",
|
||||
@"hdf" : @"application/x-hdf",
|
||||
@"heic" : @"image/heic",
|
||||
@"heif" : @"image/heif",
|
||||
@"hh" : @"text/x-c",
|
||||
@"hlp" : @"application/winhlp",
|
||||
@"hpgl" : @"application/vnd.hp-hpgl",
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#import "MIMETypeUtil.h"
|
||||
#import "OWSFileSystem.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <libwebp/decode.h>
|
||||
#import <libwebp/demux.h>
|
||||
#import <SessionUtilitiesKit/SessionUtilitiesKit-Swift.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
@ -13,8 +15,18 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
ImageFormat_Tiff,
|
||||
ImageFormat_Jpeg,
|
||||
ImageFormat_Bmp,
|
||||
ImageFormat_Webp,
|
||||
ImageFormat_Heic,
|
||||
ImageFormat_Heif,
|
||||
};
|
||||
|
||||
#pragma mark -
|
||||
|
||||
typedef struct {
|
||||
CGSize pixelSize;
|
||||
CGFloat depthBytes;
|
||||
} ImageDimensionInfo;
|
||||
|
||||
// FIXME: Refactor all of these to be in Swift against 'Data'
|
||||
@implementation NSData (Image)
|
||||
|
||||
|
@ -47,40 +59,47 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
return YES;
|
||||
}
|
||||
|
||||
+ (BOOL)ows_isValidImageAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType
|
||||
+ (nullable NSData *)ows_validImageDataAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType
|
||||
{
|
||||
if (mimeType.length < 1) {
|
||||
NSString *fileExtension = [filePath pathExtension].lowercaseString;
|
||||
mimeType = [MIMETypeUtil mimeTypeForFileExtension:fileExtension];
|
||||
}
|
||||
if (mimeType.length < 1) {
|
||||
return NO;
|
||||
return nil;
|
||||
}
|
||||
NSNumber *_Nullable fileSize = [OWSFileSystem fileSizeOfPath:filePath];
|
||||
if (!fileSize) {
|
||||
return NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
BOOL isAnimated = [MIMETypeUtil isSupportedAnimatedMIMEType:mimeType];
|
||||
if (isAnimated) {
|
||||
if (fileSize.unsignedIntegerValue > OWSMediaUtils.kMaxFileSizeAnimatedImage) {
|
||||
return NO;
|
||||
return nil;
|
||||
}
|
||||
} else if ([MIMETypeUtil isSupportedImageMIMEType:mimeType]) {
|
||||
if (fileSize.unsignedIntegerValue > OWSMediaUtils.kMaxFileSizeImage) {
|
||||
return NO;
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
return NO;
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSError *error = nil;
|
||||
NSData *_Nullable data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error];
|
||||
if (!data || error) {
|
||||
return [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error];
|
||||
}
|
||||
|
||||
+ (BOOL)ows_isValidImageAtPath:(NSString *)filePath mimeType:(nullable NSString *)mimeType
|
||||
{
|
||||
NSData *_Nullable data = [NSData ows_validImageDataAtPath:filePath mimeType:mimeType];
|
||||
if (!data) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![self ows_hasValidImageDimensionsAtPath:filePath isAnimated:isAnimated]) {
|
||||
BOOL isAnimated = [MIMETypeUtil isSupportedAnimatedMIMEType:mimeType];
|
||||
|
||||
if (![self ows_hasValidImageDimensionsAtPath:filePath withData:data mimeType:mimeType isAnimated:isAnimated]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
@ -93,45 +112,98 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
if (imageSource == NULL) {
|
||||
return NO;
|
||||
}
|
||||
BOOL result = [NSData ows_hasValidImageDimensionWithImageSource:imageSource isAnimated:isAnimated];
|
||||
|
||||
ImageDimensionInfo dimensionInfo = [NSData ows_imageDimensionWithImageSource:imageSource isAnimated:isAnimated];
|
||||
CFRelease(imageSource);
|
||||
return result;
|
||||
|
||||
return [NSData ows_isValidImageDimension:dimensionInfo.pixelSize depthBytes:dimensionInfo.depthBytes isAnimated:isAnimated];
|
||||
}
|
||||
|
||||
+ (BOOL)ows_hasValidImageDimensionsAtPath:(NSString *)path isAnimated:(BOOL)isAnimated
|
||||
+ (BOOL)ows_hasValidImageDimensionsAtPath:(NSString *)path withData:(NSData *)data mimeType:(nullable NSString *)mimeType isAnimated:(BOOL)isAnimated
|
||||
{
|
||||
CGSize imageDimensions = [self ows_imageDimensionsAtPath:path withData:data mimeType:mimeType isAnimated:isAnimated];
|
||||
|
||||
if (imageDimensions.width < 1 || imageDimensions.height < 1) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
+ (CGSize)ows_imageDimensionsAtPath:(NSString *)path withData:(nullable NSData *)data mimeType:(nullable NSString *)mimeType isAnimated:(BOOL)isAnimated
|
||||
{
|
||||
NSURL *url = [NSURL fileURLWithPath:path];
|
||||
if (!url) {
|
||||
return NO;
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
if ([mimeType isEqualToString:OWSMimeTypeImageWebp]) {
|
||||
NSData *targetData = data;
|
||||
|
||||
if (targetData == nil) {
|
||||
NSError *error = nil;
|
||||
NSData *_Nullable loadedData = [NSData dataWithContentsOfFile:path options:NSDataReadingMappedIfSafe error:&error];
|
||||
|
||||
if (!data || error) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
targetData = loadedData;
|
||||
}
|
||||
|
||||
CGSize imageSize = [data sizeForWebpData];
|
||||
|
||||
if (imageSize.width < 1 || imageSize.height < 1) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
const CGFloat kExpectedBytePerPixel = 4;
|
||||
CGFloat kMaxValidImageDimension = OWSMediaUtils.kMaxAnimatedImageDimensions;
|
||||
CGFloat kMaxBytes = kMaxValidImageDimension * kMaxValidImageDimension * kExpectedBytePerPixel;
|
||||
|
||||
if (data.length > kMaxBytes) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
return imageSize;
|
||||
}
|
||||
|
||||
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)url, NULL);
|
||||
if (imageSource == NULL) {
|
||||
return NO;
|
||||
return CGSizeZero;
|
||||
}
|
||||
BOOL result = [self ows_hasValidImageDimensionWithImageSource:imageSource isAnimated:isAnimated];
|
||||
|
||||
ImageDimensionInfo dimensionInfo = [self ows_imageDimensionWithImageSource:imageSource isAnimated:isAnimated];
|
||||
CFRelease(imageSource);
|
||||
return result;
|
||||
|
||||
if (![self ows_isValidImageDimension:dimensionInfo.pixelSize depthBytes:dimensionInfo.depthBytes isAnimated:isAnimated]) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
return dimensionInfo.pixelSize;
|
||||
}
|
||||
|
||||
+ (BOOL)ows_hasValidImageDimensionWithImageSource:(CGImageSourceRef)imageSource isAnimated:(BOOL)isAnimated
|
||||
+ (ImageDimensionInfo)ows_imageDimensionWithImageSource:(CGImageSourceRef)imageSource isAnimated:(BOOL)isAnimated
|
||||
{
|
||||
NSDictionary *imageProperties
|
||||
= (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
|
||||
ImageDimensionInfo info;
|
||||
info.pixelSize = CGSizeZero;
|
||||
info.depthBytes = 0;
|
||||
|
||||
if (!imageProperties) {
|
||||
return NO;
|
||||
return info;
|
||||
}
|
||||
|
||||
NSNumber *widthNumber = imageProperties[(__bridge NSString *)kCGImagePropertyPixelWidth];
|
||||
if (!widthNumber) {
|
||||
return NO;
|
||||
return info;
|
||||
}
|
||||
CGFloat width = widthNumber.floatValue;
|
||||
|
||||
NSNumber *heightNumber = imageProperties[(__bridge NSString *)kCGImagePropertyPixelHeight];
|
||||
if (!heightNumber) {
|
||||
return NO;
|
||||
return info;
|
||||
}
|
||||
CGFloat height = heightNumber.floatValue;
|
||||
|
||||
|
@ -139,7 +211,7 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
* key is a CFNumberRef. */
|
||||
NSNumber *depthNumber = imageProperties[(__bridge NSString *)kCGImagePropertyDepth];
|
||||
if (!depthNumber) {
|
||||
return NO;
|
||||
return info;
|
||||
}
|
||||
NSUInteger depthBits = depthNumber.unsignedIntegerValue;
|
||||
// This should usually be 1.
|
||||
|
@ -149,13 +221,27 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
* The value of this key is CFStringRef. */
|
||||
NSString *colorModel = imageProperties[(__bridge NSString *)kCGImagePropertyColorModel];
|
||||
if (!colorModel) {
|
||||
return NO;
|
||||
return info;
|
||||
}
|
||||
if (![colorModel isEqualToString:(__bridge NSString *)kCGImagePropertyColorModelRGB]
|
||||
&& ![colorModel isEqualToString:(__bridge NSString *)kCGImagePropertyColorModelGray]) {
|
||||
return info;
|
||||
}
|
||||
|
||||
// Update the struct to return
|
||||
info.pixelSize = CGSizeMake(width, height);
|
||||
info.depthBytes = depthBytes;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
+ (BOOL)ows_isValidImageDimension:(CGSize)imageSize depthBytes:(CGFloat)depthBytes isAnimated:(BOOL)isAnimated
|
||||
{
|
||||
if (imageSize.width < 1 || imageSize.height < 1 || depthBytes < 1) {
|
||||
// Invalid metadata.
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
// We only support (A)RGB and (A)Grayscale, so worst case is 4.
|
||||
const CGFloat kWorseCastComponentsPerPixel = 4;
|
||||
CGFloat bytesPerPixel = kWorseCastComponentsPerPixel * depthBytes;
|
||||
|
@ -164,7 +250,7 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
CGFloat kMaxValidImageDimension
|
||||
= (isAnimated ? OWSMediaUtils.kMaxAnimatedImageDimensions : OWSMediaUtils.kMaxStillImageDimensions);
|
||||
CGFloat kMaxBytes = kMaxValidImageDimension * kMaxValidImageDimension * kExpectedBytePerPixel;
|
||||
CGFloat actualBytes = width * height * bytesPerPixel;
|
||||
CGFloat actualBytes = imageSize.width * imageSize.height * bytesPerPixel;
|
||||
if (actualBytes > kMaxBytes) {
|
||||
return NO;
|
||||
}
|
||||
|
@ -205,6 +291,12 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
case ImageFormat_Bmp:
|
||||
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageBmp1] ||
|
||||
[mimeType isEqualToString:OWSMimeTypeImageBmp2]);
|
||||
case ImageFormat_Webp:
|
||||
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageWebp]);
|
||||
case ImageFormat_Heic:
|
||||
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageHeic]);
|
||||
case ImageFormat_Heif:
|
||||
return (mimeType == nil || [mimeType isEqualToString:OWSMimeTypeImageHeif]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,9 +327,52 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
} else if (byte0 == 0x49 && byte1 == 0x49) {
|
||||
// Intel byte order TIFF
|
||||
return ImageFormat_Tiff;
|
||||
} else if (byte0 == 0x52 && byte1 == 0x49) {
|
||||
// First two letters of RIFF tag.
|
||||
return ImageFormat_Webp;
|
||||
}
|
||||
|
||||
return [self ows_guessHighEfficiencyImageFormat];
|
||||
}
|
||||
|
||||
- (ImageFormat)ows_guessHighEfficiencyImageFormat
|
||||
{
|
||||
// A HEIF image file has the first 16 bytes like
|
||||
// 0000 0018 6674 7970 6865 6963 0000 0000
|
||||
// so in this case the 5th to 12th bytes shall make a string of "ftypheic"
|
||||
const NSUInteger kHeifHeaderStartsAt = 4;
|
||||
const NSUInteger kHeifBrandStartsAt = 8;
|
||||
// We support "heic", "mif1" or "msf1". Other brands are invalid for us for now.
|
||||
// The length is 4 + 1 because the brand must be terminated with a null.
|
||||
// Include the null in the comparison to prevent a bogus brand like "heicfake"
|
||||
// from being considered valid.
|
||||
const NSUInteger kHeifSupportedBrandLength = 5;
|
||||
const NSUInteger kTotalHeaderLength = kHeifBrandStartsAt - kHeifHeaderStartsAt + kHeifSupportedBrandLength;
|
||||
if (self.length < kHeifBrandStartsAt + kHeifSupportedBrandLength) {
|
||||
return ImageFormat_Unknown;
|
||||
}
|
||||
|
||||
return ImageFormat_Unknown;
|
||||
// These are the brands of HEIF formatted files that are renderable by CoreGraphics
|
||||
const NSString *kHeifBrandHeaderHeic = @"ftypheic\0";
|
||||
const NSString *kHeifBrandHeaderHeif = @"ftypmif1\0";
|
||||
const NSString *kHeifBrandHeaderHeifStream = @"ftypmsf1\0";
|
||||
|
||||
// Pull the string from the header and compare it with the supported formats
|
||||
unsigned char bytes[kTotalHeaderLength];
|
||||
[self getBytes:&bytes range:NSMakeRange(kHeifHeaderStartsAt, kTotalHeaderLength)];
|
||||
NSData *data = [[NSData alloc] initWithBytes:bytes length:kTotalHeaderLength];
|
||||
NSString *marker = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
|
||||
if ([kHeifBrandHeaderHeic isEqualToString:marker]) {
|
||||
return ImageFormat_Heic;
|
||||
} else if ([kHeifBrandHeaderHeif isEqualToString:marker]) {
|
||||
return ImageFormat_Heif;
|
||||
} else if ([kHeifBrandHeaderHeifStream isEqualToString:marker]) {
|
||||
return ImageFormat_Heif;
|
||||
} else {
|
||||
return ImageFormat_Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *_Nullable)ows_guessMimeType
|
||||
|
@ -304,9 +439,18 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
|
||||
+ (CGSize)imageSizeForFilePath:(NSString *)filePath mimeType:(NSString *)mimeType
|
||||
{
|
||||
if (![NSData ows_isValidImageAtPath:filePath mimeType:mimeType]) {
|
||||
NSData *_Nullable data = [NSData ows_validImageDataAtPath:filePath mimeType:mimeType];
|
||||
if (!data) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
BOOL isAnimated = [MIMETypeUtil isSupportedAnimatedMIMEType:mimeType];
|
||||
CGSize pixelSize = [NSData ows_imageDimensionsAtPath:filePath withData:data mimeType:mimeType isAnimated:isAnimated];
|
||||
|
||||
if (pixelSize.width > 0 && pixelSize.height > 0 && [mimeType isEqualToString:OWSMimeTypeImageWebp]) {
|
||||
return pixelSize;
|
||||
}
|
||||
|
||||
NSURL *url = [NSURL fileURLWithPath:filePath];
|
||||
|
||||
// With CGImageSource we avoid loading the whole image into memory.
|
||||
|
@ -386,6 +530,42 @@ typedef NS_ENUM(NSInteger, ImageFormat) {
|
|||
return result;
|
||||
}
|
||||
|
||||
// MARK: - Webp
|
||||
|
||||
+ (CGSize)sizeForWebpFilePath:(NSString *)filePath
|
||||
{
|
||||
NSError *error = nil;
|
||||
NSData *_Nullable data = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&error];
|
||||
if (!data || error) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
return [data sizeForWebpData];
|
||||
}
|
||||
|
||||
- (CGSize)sizeForWebpData
|
||||
{
|
||||
WebPData webPData = { 0 };
|
||||
webPData.bytes = self.bytes;
|
||||
webPData.size = self.length;
|
||||
WebPDemuxer *demuxer = WebPDemux(&webPData);
|
||||
|
||||
if (!demuxer) {
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
CGFloat canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
|
||||
CGFloat canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
|
||||
CGFloat frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
|
||||
|
||||
WebPDemuxDelete(demuxer);
|
||||
|
||||
if (canvasWidth > 0 && canvasHeight > 0 && frameCount > 0) {
|
||||
return CGSizeMake(canvasWidth, canvasHeight);
|
||||
}
|
||||
|
||||
return CGSizeZero;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
Loading…
Reference in New Issue