diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index 1c36fd9e0..1d9e3f41f 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -2,6 +2,7 @@ import UIKit import CoreServices import Photos import PhotosUI +import SessionUtilitiesKit import SignalUtilitiesKit extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuActionDelegate, ScrollToBottomButtonDelegate, @@ -516,7 +517,18 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc let thread = self.thread as? TSContactThread, Storage.shared.getContact(with: thread.contactSessionID())?.isTrusted != true { confirmDownload() - } else { + } + else if ( + viewItem.attachmentStream?.isText == true || + viewItem.attachmentStream?.isMicrosoftDoc == true || + viewItem.attachmentStream?.contentType == OWSMimeTypeApplicationPdf + ), let filePathString: String = viewItem.attachmentStream?.originalFilePath { + let fileUrl: URL = URL(fileURLWithPath: filePathString) + let interactionController: UIDocumentInteractionController = UIDocumentInteractionController(url: fileUrl) + interactionController.delegate = self + interactionController.presentPreview(animated: true) + } + else { // Open the document if possible guard let url = viewItem.attachmentStream?.originalMediaURL else { return } let shareVC = UIActivityViewController(activityItems: [ url ], applicationActivities: nil) @@ -1039,6 +1051,14 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } } +// MARK: - UIDocumentInteractionControllerDelegate + +extension ConversationVC: UIDocumentInteractionControllerDelegate { + func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController { + return self + } +} + // MARK: - Message Request Actions extension ConversationVC { diff --git a/Session/Meta/AppDelegate.m b/Session/Meta/AppDelegate.m index 000af7460..1f31d6604 100644 --- a/Session/Meta/AppDelegate.m +++ b/Session/Meta/AppDelegate.m @@ -194,7 +194,7 @@ static NSTimeInterval launchStartedAt; mainWindow.rootViewController = [LoadingViewController new]; [mainWindow makeKeyAndVisible]; - LKAppMode appMode = [self getCurrentAppMode]; + LKAppMode appMode = [LKAppModeManager getAppModeOrSystemDefault]; [self adaptAppMode:appMode]; if (@available(iOS 11, *)) { @@ -245,7 +245,7 @@ static NSTimeInterval launchStartedAt; [self ensureRootViewController]; - LKAppMode appMode = [self getCurrentAppMode]; + LKAppMode appMode = [LKAppModeManager getAppModeOrSystemDefault]; [self adaptAppMode:appMode]; [AppReadiness runNowOrWhenAppDidBecomeReady:^{ @@ -734,12 +734,6 @@ static NSTimeInterval launchStartedAt; [NSNotificationCenter.defaultCenter postNotificationName:NSNotification.appModeChanged object:nil]; } -- (LKAppMode)getCurrentAppMode -{ - LKAppMode appMode = [self getAppModeOrSystemDefault]; - return appMode; -} - - (void)setCurrentAppMode:(LKAppMode)appMode { [NSUserDefaults.standardUserDefaults setInteger:appMode forKey:@"appMode"]; @@ -749,7 +743,7 @@ static NSTimeInterval launchStartedAt; - (void)setAppModeToSystemDefault { [NSUserDefaults.standardUserDefaults removeObjectForKey:@"appMode"]; - LKAppMode appMode = [self getCurrentAppMode]; + LKAppMode appMode = [LKAppModeManager getAppModeOrSystemDefault]; [self adaptAppMode:appMode]; } diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index b12a3d757..9a66ad3d3 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -45,20 +45,4 @@ extension AppDelegate { @objc func stopClosedGroupPoller() { ClosedGroupPoller.shared.stop() } - - @objc func getAppModeOrSystemDefault() -> AppMode { - let userDefaults = UserDefaults.standard - - guard userDefaults.dictionaryRepresentation().keys.contains("appMode") else { - if #available(iOS 13.0, *) { - return UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light - } else { - return .light - } - } - - let mode = userDefaults.integer(forKey: "appMode") - return AppMode(rawValue: mode) ?? .light - } - } diff --git a/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift b/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift index f65d8640c..0087c3663 100644 --- a/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift +++ b/SessionMessagingKit/Sending & Receiving/Attachments/SignalAttachment.swift @@ -462,7 +462,12 @@ public class SignalAttachment: NSObject { @objc public var isText: Bool { - return UTTypeConformsTo(dataUTI as CFString, kUTTypeText) || isOversizeText + return ( + isConvertibleToTextMessage && ( + UTTypeConformsTo(dataUTI as CFString, kUTTypeText) || + isOversizeText + ) + ) } @objc diff --git a/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.h b/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.h index 89f3b4aae..d8f0d8936 100644 --- a/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.h +++ b/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.h @@ -84,6 +84,8 @@ typedef NS_ENUM(NSUInteger, TSAttachmentType) { @property (nonatomic, readonly) BOOL isAudio; @property (nonatomic, readonly) BOOL isVoiceMessage; @property (nonatomic, readonly) BOOL isVisualMedia; +@property (nonatomic, readonly) BOOL isText; +@property (nonatomic, readonly) BOOL isMicrosoftDoc; @property (nonatomic, readonly) BOOL isOversizeText; + (NSString *)emojiForMimeType:(NSString *)contentType; diff --git a/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.m b/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.m index e32418bdf..f3722bd80 100644 --- a/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.m +++ b/SessionMessagingKit/Sending & Receiving/Attachments/TSAttachment.m @@ -3,6 +3,14 @@ #import "TSAttachmentPointer.h" #import +#if TARGET_OS_IPHONE +#import + +#else +#import + +#endif + NS_ASSUME_NONNULL_BEGIN NSUInteger const TSAttachmentSchemaVersion = 4; @@ -229,6 +237,14 @@ NSUInteger const TSAttachmentSchemaVersion = 4; return [MIMETypeUtil isVisualMedia:self.contentType]; } +- (BOOL)isText { + return [MIMETypeUtil isText:self.contentType]; +} + +- (BOOL)isMicrosoftDoc { + return [MIMETypeUtil isMicrosoftDoc:self.contentType]; +} + - (BOOL)isOversizeText { return [self.contentType isEqualToString:OWSMimeTypeOversizeTextMessage]; diff --git a/SessionShareExtension/ShareVC.swift b/SessionShareExtension/ShareVC.swift index 1492f8d80..34716c706 100644 --- a/SessionShareExtension/ShareVC.swift +++ b/SessionShareExtension/ShareVC.swift @@ -162,15 +162,6 @@ final class ShareVC : UINavigationController, ShareViewDelegate, AppModeManagerD } // MARK: - App Mode - - public func getCurrentAppMode() -> AppMode { - guard let window = self.view.window else { return .light } - - let userInterfaceStyle = window.traitCollection.userInterfaceStyle - let isLightMode = (userInterfaceStyle == .light || userInterfaceStyle == .unspecified) - - return (isLightMode ? .light : .dark) - } public func setCurrentAppMode(to appMode: AppMode) { return // Not applicable to share extensions diff --git a/SessionShareExtension/ThreadPickerVC.swift b/SessionShareExtension/ThreadPickerVC.swift index 883484405..0e0ff5db3 100644 --- a/SessionShareExtension/ThreadPickerVC.swift +++ b/SessionShareExtension/ThreadPickerVC.swift @@ -95,6 +95,11 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView } } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + view.setGradient(Gradients.defaultBackground) + fadeView.setGradient(Gradients.homeVCFade) + } + // MARK: Layout private func setupLayout() { diff --git a/SessionUIKit/Style Guide/AppMode.swift b/SessionUIKit/Style Guide/AppMode.swift index 01db04682..e2ad9e75a 100644 --- a/SessionUIKit/Style Guide/AppMode.swift +++ b/SessionUIKit/Style Guide/AppMode.swift @@ -1,11 +1,11 @@ -import Foundation +import UIKit @objc(LKAppModeManager) public final class AppModeManager : NSObject { private let delegate: AppModeManagerDelegate public var currentAppMode: AppMode { - return delegate.getCurrentAppMode() + return AppModeManager.getAppModeOrSystemDefault() } public static var shared: AppModeManager! @@ -29,19 +29,33 @@ public final class AppModeManager : NSObject { public func setAppModeToSystemDefault() { delegate.setAppModeToSystemDefault() } + + @objc public static func getAppModeOrSystemDefault() -> AppMode { + let userDefaults = UserDefaults.standard + + guard userDefaults.dictionaryRepresentation().keys.contains("appMode") else { + if #available(iOS 13.0, *) { + return UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light + } + + return .light + } + + let mode = userDefaults.integer(forKey: "appMode") + return AppMode(rawValue: mode) ?? .light + } } @objc(LKAppModeManagerDelegate) public protocol AppModeManagerDelegate { - func getCurrentAppMode() -> AppMode @objc(setCurrentAppMode:) func setCurrentAppMode(to appMode: AppMode) func setAppModeToSystemDefault() } @objc(LKAppMode) -public enum AppMode : Int { +public enum AppMode: Int { case light, dark } diff --git a/SessionUtilitiesKit/Media/MIMETypeUtil.h b/SessionUtilitiesKit/Media/MIMETypeUtil.h index 1c12bc7fa..6b09c6437 100644 --- a/SessionUtilitiesKit/Media/MIMETypeUtil.h +++ b/SessionUtilitiesKit/Media/MIMETypeUtil.h @@ -4,6 +4,7 @@ NS_ASSUME_NONNULL_BEGIN extern NSString *const OWSMimeTypeApplicationOctetStream; extern NSString *const OWSMimeTypeApplicationZip; +extern NSString *const OWSMimeTypeApplicationPdf; extern NSString *const OWSMimeTypeImagePng; extern NSString *const OWSMimeTypeImageJpeg; extern NSString *const OWSMimeTypeImageGif; @@ -40,6 +41,8 @@ extern NSString *const kSyncMessageFileExtension; + (BOOL)isImage:(NSString *)contentType; + (BOOL)isVideo:(NSString *)contentType; + (BOOL)isAudio:(NSString *)contentType; ++ (BOOL)isText:(NSString *)contentType; ++ (BOOL)isMicrosoftDoc:(NSString *)contentType; + (BOOL)isVisualMedia:(NSString *)contentType; // filename is optional and should not be trusted. diff --git a/SessionUtilitiesKit/Media/MIMETypeUtil.m b/SessionUtilitiesKit/Media/MIMETypeUtil.m index d2024d6ad..d1aa2ce6c 100644 --- a/SessionUtilitiesKit/Media/MIMETypeUtil.m +++ b/SessionUtilitiesKit/Media/MIMETypeUtil.m @@ -22,6 +22,7 @@ NSString *const OWSMimeTypeImageBmp2 = @"image/x-windows-bmp"; NSString *const OWSMimeTypeOversizeTextMessage = @"text/x-signal-plain"; NSString *const OWSMimeTypeUnknownForTests = @"unknown/mimetype"; NSString *const OWSMimeTypeApplicationZip = @"application/zip"; +NSString *const OWSMimeTypeApplicationPdf = @"application/pdf"; NSString *const kOversizeTextAttachmentUTI = @"org.whispersystems.oversize-text-attachment"; NSString *const kOversizeTextAttachmentFileExtension = @"txt"; @@ -278,6 +279,53 @@ NSString *const kSyncMessageFileExtension = @"bin"; return [MIMETypeUtil isSupportedAudioMIMEType:contentType]; } ++ (BOOL)isText:(NSString *)contentType { + return [ + @[ + @"text/plain", + @"text/csv", + @"text/tab-separated-values" + ] + containsObject:contentType + ]; +} + ++ (BOOL)isMicrosoftDoc:(NSString *)contentType { + return [ + @[ + // Word files + @"application/msword", + + @"application/vnd.openxmlformats-officedocument.wordprocessingml.document", + @"application/vnd.openxmlformats-officedocument.wordprocessingml.template", + @"application/vnd.ms-word.document.macroEnabled.12", + @"application/vnd.ms-word.template.macroEnabled.12", + + // Excel files + @"application/vnd.ms-excel", + + @"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + @"application/vnd.openxmlformats-officedocument.spreadsheetml.template", + @"application/vnd.ms-excel.sheet.macroEnabled.12", + @"application/vnd.ms-excel.template.macroEnabled.12", + @"application/vnd.ms-excel.addin.macroEnabled.12", + @"application/vnd.ms-excel.sheet.binary.macroEnabled.12", + + // Powerpoint files + @"application/vnd.ms-powerpoint", + + @"application/vnd.openxmlformats-officedocument.presentationml.presentation", + @"application/vnd.openxmlformats-officedocument.presentationml.template", + @"application/vnd.openxmlformats-officedocument.presentationml.slideshow", + @"application/vnd.ms-powerpoint.addin.macroEnabled.12", + @"application/vnd.ms-powerpoint.presentation.macroEnabled.12", + @"application/vnd.ms-powerpoint.template.macroEnabled.12", + @"application/vnd.ms-powerpoint.slideshow.macroEnabled.12" + ] + containsObject:contentType + ]; +} + + (BOOL)isVisualMedia:(NSString *)contentType { if ([self isImage:contentType]) {