diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index 8f5ef4d5f..64b11798c 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -1,5 +1,6 @@ import CoreServices import Photos +import PhotosUI extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuActionDelegate, ScrollToBottomButtonDelegate, SendMediaNavDelegate, UIDocumentPickerDelegate, AttachmentApprovalViewControllerDelegate, GifPickerViewControllerDelegate, @@ -91,12 +92,14 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } func handleLibraryButtonTapped() { - // FIXME: We're not yet handling the case where the user only gives access to selected photos/videos - guard requestLibraryPermissionIfNeeded() else { return } - let sendMediaNavController = SendMediaNavigationController.showingMediaLibraryFirst() - sendMediaNavController.sendMediaNavDelegate = self - sendMediaNavController.modalPresentationStyle = .fullScreen - present(sendMediaNavController, animated: true, completion: nil) + requestLibraryPermissionIfNeeded { [weak self] in + DispatchQueue.main.async { + let sendMediaNavController = SendMediaNavigationController.showingMediaLibraryFirst() + sendMediaNavController.sendMediaNavDelegate = self + sendMediaNavController.modalPresentationStyle = .fullScreen + self?.present(sendMediaNavController, animated: true, completion: nil) + } + } } func handleGIFButtonTapped() { @@ -752,19 +755,54 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } } - func requestLibraryPermissionIfNeeded() -> Bool { - switch PHPhotoLibrary.authorizationStatus() { - case .authorized, .limited: return true + func requestLibraryPermissionIfNeeded(onAuthorized: @escaping () -> Void) { + let authorizationStatus: PHAuthorizationStatus + if #available(iOS 14, *) { + authorizationStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite) + if authorizationStatus == .notDetermined { + // When the user chooses to select photos (which is the .limit status), + // the PHPhotoUI will present the picker view on the top of the front view. + // Since we have the ScreenLockUI showing when we request premissions, + // the picker view will be presented on the top of the ScreenLockUI. + // However, the ScreenLockUI will dismiss with the permission request alert view, so + // the picker view then will dismiss, too. The selection process cannot be finished + // this way. So we add a flag (isRequestingPermission) to prevent the ScreenLockUI + // from showing when we request the photo library permission. + Environment.shared.isRequestingPermission = true + let appMode = AppModeManager.shared.currentAppMode + // FIXME: Rather than setting the app mode to light and then to dark again once we're done, + // it'd be better to just customize the appearance of the image picker. There doesn't currently + // appear to be a good way to do so though... + AppModeManager.shared.setCurrentAppMode(to: .light) + PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in + DispatchQueue.main.async { + AppModeManager.shared.setCurrentAppMode(to: appMode) + } + Environment.shared.isRequestingPermission = false + if [ PHAuthorizationStatus.authorized, PHAuthorizationStatus.limited ].contains(status) { + onAuthorized() + } + } + } + } else { + authorizationStatus = PHPhotoLibrary.authorizationStatus() + if authorizationStatus == .notDetermined { + PHPhotoLibrary.requestAuthorization { status in + if status == .authorized { + onAuthorized() + } + } + } + } + switch authorizationStatus { + case .authorized, .limited: + onAuthorized() case .denied, .restricted: let modal = PermissionMissingModal(permission: "library") { } modal.modalPresentationStyle = .overFullScreen modal.modalTransitionStyle = .crossDissolve present(modal, animated: true, completion: nil) - return false - case .notDetermined: - PHPhotoLibrary.requestAuthorization { _ in } - return false - default: return false + default: return } } diff --git a/Session/Conversations/ConversationViewModel.m b/Session/Conversations/ConversationViewModel.m index cee8ff67e..b893fe7f5 100644 --- a/Session/Conversations/ConversationViewModel.m +++ b/Session/Conversations/ConversationViewModel.m @@ -173,7 +173,7 @@ NS_ASSUME_NONNULL_BEGIN static const int kYapDatabasePageSize = 100; // Never show more than n messages in conversation view when user arrives. -static const int kConversationInitialMaxRangeSize = 25000; // TODO: Does it cause issues to set this so high? +static const int kConversationInitialMaxRangeSize = 100; // Never show more than n messages in conversation view at a time. static const int kYapDatabaseRangeMaxLength = 25000; diff --git a/Session/Meta/Session-Info.plist b/Session/Meta/Session-Info.plist index 727fb665f..875ffe05b 100644 --- a/Session/Meta/Session-Info.plist +++ b/Session/Meta/Session-Info.plist @@ -2,6 +2,8 @@ + PHPhotoLibraryPreventAutomaticLimitedAccessAlert + BuildDetails CarthageVersion @@ -80,7 +82,7 @@ NSContactsUsageDescription Signal uses your contacts to find users you know. We do not store your contacts on the server. NSFaceIDUsageDescription - Session's Screen Lock feature uses Face ID. + Session's Screen Lock feature uses Face ID. NSMicrophoneUsageDescription Session needs access to your microphone to record media. NSPhotoLibraryAddUsageDescription diff --git a/Session/Shared/OWSScreenLockUI.m b/Session/Shared/OWSScreenLockUI.m index 65bf6f28e..e7cb44121 100644 --- a/Session/Shared/OWSScreenLockUI.m +++ b/Session/Shared/OWSScreenLockUI.m @@ -347,6 +347,10 @@ NS_ASSUME_NONNULL_BEGIN OWSLogVerbose(@"desiredUIState: none 3."); return ScreenLockUIStateNone; } + + if (Environment.shared.isRequestingPermission) { + return ScreenLockUIStateNone; + } if (Environment.shared.preferences.screenSecurityIsEnabled) { OWSLogVerbose(@"desiredUIState: screen protection 4."); diff --git a/SessionMessagingKit/Utilities/Environment.h b/SessionMessagingKit/Utilities/Environment.h index 64d7ddc61..edbd77376 100644 --- a/SessionMessagingKit/Utilities/Environment.h +++ b/SessionMessagingKit/Utilities/Environment.h @@ -29,6 +29,8 @@ @property (nonatomic, readonly) OWSPreferences *preferences; @property (nonatomic, readonly) OWSSounds *sounds; @property (nonatomic, readonly) OWSWindowManager *windowManager; +// We don't want to cover the window when we request the photo library permission +@property (nonatomic, readwrite) BOOL isRequestingPermission; @property (class, nonatomic) Environment *shared; diff --git a/SessionMessagingKit/Utilities/Environment.m b/SessionMessagingKit/Utilities/Environment.m index e4abb4602..81a39ccf2 100644 --- a/SessionMessagingKit/Utilities/Environment.m +++ b/SessionMessagingKit/Utilities/Environment.m @@ -58,6 +58,7 @@ static Environment *sharedEnvironment = nil; _proximityMonitoringManager = proximityMonitoringManager; _sounds = sounds; _windowManager = windowManager; + _isRequestingPermission = false; return self; } diff --git a/SignalUtilitiesKit/Utilities/ThreadUtil.m b/SignalUtilitiesKit/Utilities/ThreadUtil.m index 68a52e67e..62a5963fe 100644 --- a/SignalUtilitiesKit/Utilities/ThreadUtil.m +++ b/SignalUtilitiesKit/Utilities/ThreadUtil.m @@ -198,14 +198,6 @@ NS_ASSUME_NONNULL_BEGIN visibleUnseenMessageCount++; interactionAfterUnreadIndicator = interaction; - - if (visibleUnseenMessageCount + 1 >= maxRangeSize) { - // If there are more unseen messages than can be displayed in the - // messages view, show the unread indicator at the top of the - // displayed messages. - *stop = YES; - hasMoreUnseenMessages = YES; - } }]; if (!interactionAfterUnreadIndicator) {