diff --git a/ios/MattermostShare/ShareViewController.swift b/ios/MattermostShare/ShareViewController.swift index 318850812..898a1ee13 100644 --- a/ios/MattermostShare/ShareViewController.swift +++ b/ios/MattermostShare/ShareViewController.swift @@ -44,10 +44,16 @@ class ShareViewController: SLComposeServiceViewController { override func isContentValid() -> Bool { // Do validation of contentText and/or NSExtensionContext attachments here if (attachments.count > 0) { + let maxImagePixels = store.getMaxImagePixels() + if attachments.hasImageLargerThan(pixels: maxImagePixels) { + let readableMaxImagePixels = formatImagePixels(pixels: maxImagePixels) + showErrorMessage(title: "", message: "Image attachments shared in Mattermost must be less than \(readableMaxImagePixels).", VC: self) + } + let maxFileSize = store.getMaxFileSize() if attachments.hasAttachementLargerThan(fileSize: maxFileSize) { - let readableMaxSize = formatFileSize(bytes: Double(maxFileSize)) - showErrorMessage(title: "", message: "File attachments shared in Mattermost must be less than \(readableMaxSize).", VC: self) + let readableMaxFileSize = formatFileSize(fileSize: maxFileSize) + showErrorMessage(title: "", message: "File attachments shared in Mattermost must be less than \(readableMaxFileSize).", VC: self) } } @@ -169,6 +175,26 @@ class ShareViewController: SLComposeServiceViewController { } return section } + + func getImagePixels(imageUrl: URL) -> UInt64 { + guard let imageData = try? Data(contentsOf: imageUrl) else { + return 0 + } + + guard let image = UIImage.init(data: imageData) else { + return 0 + } + + return getImagePixels(image: image) + } + + func getImagePixels(image: UIImage) -> UInt64 { + guard let cgImage = image.cgImage else { + return 0 + } + + return UInt64(cgImage.width * cgImage.height) + } func extractDataFromContext() { for item in extensionContext?.inputItems as! [NSExtensionItem] { @@ -196,6 +222,7 @@ class ShareViewController: SLComposeServiceViewController { let attachment = self.saveAttachment(url: url) if (attachment != nil) { attachment?.type = kUTTypeImage as String + attachment?.imagePixels = self.getImagePixels(imageUrl: url) self.attachments.append(attachment!) } } else if let image = item as? UIImage { @@ -207,6 +234,7 @@ class ShareViewController: SLComposeServiceViewController { let attachment = self.saveAttachment(url: tempImageURL!) if (attachment != nil) { attachment?.type = kUTTypeImage as String + attachment?.imagePixels = self.getImagePixels(image: image) self.attachments.append(attachment!) } } @@ -370,6 +398,7 @@ class ShareViewController: SLComposeServiceViewController { attachment.fileName = fileName attachment.fileURL = tempFileURL attachment.fileSize = attr.fileSize() + return attachment } catch { return nil @@ -388,22 +417,32 @@ class ShareViewController: SLComposeServiceViewController { VC.present(alert, animated: true, completion: nil) } - func formatFileSize(bytes: Double) -> String { - guard bytes > 0 else { - return "0 bytes" + func formatImagePixels(pixels: UInt64) -> String { + let suffixes = ["pixels", "KP", "MP", "GP", "TP", "PP", "EP", "ZP", "YP"] + let k: Double = 1000 + return formatSize(size: Double(pixels), k: k, suffixes: suffixes) + } + + func formatFileSize(fileSize: UInt64) -> String { + let suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] + let k: Double = 1024 + return formatSize(size: Double(fileSize), k: k, suffixes: suffixes) + } + + func formatSize(size: Double, k: Double, suffixes: Array) -> String { + guard size > 0 else { + return "0 \(suffixes[0])" } // Adapted from http://stackoverflow.com/a/18650828 - let suffixes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] - let k: Double = 1024 - let i = floor(log(bytes) / log(k)) + let i = floor(log(size) / log(k)) - // Format number with thousands separator and everything below 1 GB with no decimal places. + // Format number with thousands separator and everything below 1 giga with no decimal places. let numberFormatter = NumberFormatter() numberFormatter.maximumFractionDigits = i < 3 ? 0 : 1 numberFormatter.numberStyle = .decimal - let numberString = numberFormatter.string(from: NSNumber(value: bytes / pow(k, i))) ?? "Unknown" + let numberString = numberFormatter.string(from: NSNumber(value: size / pow(k, i))) ?? "Unknown" let suffix = suffixes[Int(i)] return "\(numberString) \(suffix)" } diff --git a/ios/UploadAttachments/UploadAttachments/AttachmentArray.swift b/ios/UploadAttachments/UploadAttachments/AttachmentArray.swift index 0377a237d..f1a2d073f 100644 --- a/ios/UploadAttachments/UploadAttachments/AttachmentArray.swift +++ b/ios/UploadAttachments/UploadAttachments/AttachmentArray.swift @@ -1,3 +1,5 @@ +import MobileCoreServices + /// A thread-safe array. public class AttachmentArray: NSObject { fileprivate let queue = DispatchQueue(label: "com.mattermost.SynchronizedArray", attributes: .concurrent) @@ -266,4 +268,17 @@ extension AttachmentArray { return exceed } + + public func hasImageLargerThan(pixels: UInt64) -> Bool { + var exceed = false + + self.queue.sync { + exceed = self.array.contains { element in + let attachment = element as! AttachmentItem + return attachment.imagePixels > pixels + } + } + + return exceed + } } diff --git a/ios/UploadAttachments/UploadAttachments/AttachmentItem.swift b/ios/UploadAttachments/UploadAttachments/AttachmentItem.swift index 7baeb946d..dfd8200f8 100644 --- a/ios/UploadAttachments/UploadAttachments/AttachmentItem.swift +++ b/ios/UploadAttachments/UploadAttachments/AttachmentItem.swift @@ -5,6 +5,7 @@ public class AttachmentItem: NSObject { public var fileURL: URL? public var fileSize: UInt64 = 0 public var type: String? + public var imagePixels: UInt64 = 0 public override init() {} } diff --git a/ios/UploadAttachments/UploadAttachments/Constants.h b/ios/UploadAttachments/UploadAttachments/Constants.h index 05a791f68..65d1d1fad 100644 --- a/ios/UploadAttachments/UploadAttachments/Constants.h +++ b/ios/UploadAttachments/UploadAttachments/Constants.h @@ -3,5 +3,6 @@ @interface Constants : NSObject extern NSString *APP_GROUP_ID; +extern UInt64 DEFAULT_SERVER_MAX_IMAGE_PIXELS; extern UInt64 DEFAULT_SERVER_MAX_FILE_SIZE; @end diff --git a/ios/UploadAttachments/UploadAttachments/Constants.m b/ios/UploadAttachments/UploadAttachments/Constants.m index 4c3f899a4..4c641870a 100644 --- a/ios/UploadAttachments/UploadAttachments/Constants.m +++ b/ios/UploadAttachments/UploadAttachments/Constants.m @@ -2,5 +2,6 @@ @implementation Constants NSString *APP_GROUP_ID = @"group.com.mattermost.rnbeta"; +UInt64 DEFAULT_SERVER_MAX_IMAGE_PIXELS = 6048 * 4032; UInt64 DEFAULT_SERVER_MAX_FILE_SIZE = 50 * 1024 * 1024; @end diff --git a/ios/UploadAttachments/UploadAttachments/StoreManager.h b/ios/UploadAttachments/UploadAttachments/StoreManager.h index 8a0d282c5..5d6eba79c 100644 --- a/ios/UploadAttachments/UploadAttachments/StoreManager.h +++ b/ios/UploadAttachments/UploadAttachments/StoreManager.h @@ -14,6 +14,7 @@ -(NSString *)getCurrentUserId; -(NSDictionary *)getDefaultChannel:(NSString *)forTeamId; -(NSDictionary *)getEntities:(BOOL)loadFromFile; +-(UInt64)getMaxImagePixels; -(UInt64)getMaxFileSize; -(NSArray *)getMyTeams; -(NSString *)getServerUrl; diff --git a/ios/UploadAttachments/UploadAttachments/StoreManager.m b/ios/UploadAttachments/UploadAttachments/StoreManager.m index fc5aa319e..df9c00376 100644 --- a/ios/UploadAttachments/UploadAttachments/StoreManager.m +++ b/ios/UploadAttachments/UploadAttachments/StoreManager.m @@ -150,16 +150,31 @@ return [credentials objectForKey:@"token"]; } +-(UInt64)scanValueFromConfig:(NSDictionary *)config key:(NSString *)key { + NSString *value = [config objectForKey:key]; + NSScanner *scanner = [NSScanner scannerWithString:value]; + unsigned long long convertedValue = 0; + [scanner scanUnsignedLongLong:&convertedValue]; + return convertedValue; +} + +-(UInt64)getMaxImagePixels { + NSDictionary *config = [self getConfig]; + NSString *key = @"MaxImagePixels"; + if (config != nil && [config objectForKey:key]) { + return [self scanValueFromConfig:config key:key]; + } + + return DEFAULT_SERVER_MAX_IMAGE_PIXELS; +} + -(UInt64)getMaxFileSize { NSDictionary *config = [self getConfig]; - if (config != nil && [config objectForKey:@"MaxFileSize"]) { - NSString *maxFileSize = [config objectForKey:@"MaxFileSize"]; - NSScanner *scanner = [NSScanner scannerWithString:maxFileSize]; - unsigned long long convertedValue = 0; - [scanner scanUnsignedLongLong:&convertedValue]; - return convertedValue; + NSString *key = @"MaxFileSize"; + if (config != nil && [config objectForKey:key]) { + return [self scanValueFromConfig:config key:key]; } - + return DEFAULT_SERVER_MAX_FILE_SIZE; }