From 82d193de8246707a980a98043be3f293247818a9 Mon Sep 17 00:00:00 2001 From: Levitating Pineapple Date: Wed, 16 Oct 2024 15:47:10 +0300 Subject: [PATCH] Revert "remove AppSheet" This reverts commit 3aa1688cbd9cb931ff14c522301ee69f843cafb0. --- apps/ios/Shared/ContentView.swift | 3 +- apps/ios/Shared/SimpleXApp.swift | 1 + .../Chat/ChatItem/CIInvalidJSONView.swift | 2 +- apps/ios/Shared/Views/Chat/ChatView.swift | 10 ++-- .../Views/ChatList/ChatListNavLink.swift | 4 +- .../Shared/Views/ChatList/ChatListView.swift | 2 +- apps/ios/Shared/Views/Helpers/AppSheet.swift | 53 +++++++++++++++++++ .../Helpers/LocalAuthenticationUtils.swift | 2 + .../Views/NewChat/NewChatMenuButton.swift | 2 +- .../Views/UserSettings/UserProfilesView.swift | 4 +- apps/ios/SimpleX.xcodeproj/project.pbxproj | 4 ++ 11 files changed, 74 insertions(+), 13 deletions(-) create mode 100644 apps/ios/Shared/Views/Helpers/AppSheet.swift diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index b27b35c8eb..e85f6a664a 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -13,6 +13,7 @@ struct ContentView: View { @EnvironmentObject var chatModel: ChatModel @ObservedObject var alertManager = AlertManager.shared @ObservedObject var callController = CallController.shared + @ObservedObject var appSheetState = AppSheetState.shared @Environment(\.colorScheme) var colorScheme @EnvironmentObject var theme: AppTheme @EnvironmentObject var sceneDelegate: SceneDelegate @@ -251,7 +252,7 @@ struct ContentView: View { private func mainView() -> some View { ZStack(alignment: .top) { ChatListView(activeUserPickerSheet: $chatListUserPickerSheet) - .redacted(reason: protectScreen && scenePhase != .active ? .placeholder : RedactionReasons()) + .redacted(reason: appSheetState.redactionReasons(protectScreen)) .onAppear { requestNtfAuthorization() // Local Authentication notice is to be shown on next start after onboarding is complete diff --git a/apps/ios/Shared/SimpleXApp.swift b/apps/ios/Shared/SimpleXApp.swift index b0cdc047c0..7f2c3b5866 100644 --- a/apps/ios/Shared/SimpleXApp.swift +++ b/apps/ios/Shared/SimpleXApp.swift @@ -58,6 +58,7 @@ struct SimpleXApp: App { } .onChange(of: scenePhase) { phase in logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))") + AppSheetState.shared.scenePhaseActive = phase == .active switch (phase) { case .background: // --- authentication diff --git a/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift b/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift index 33a9176f0b..18fd682646 100644 --- a/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift +++ b/apps/ios/Shared/Views/Chat/ChatItem/CIInvalidJSONView.swift @@ -24,7 +24,7 @@ struct CIInvalidJSONView: View { .background(Color(uiColor: .tertiarySystemGroupedBackground)) .textSelection(.disabled) .onTapGesture { showJSON = true } - .sheet(isPresented: $showJSON) { + .appSheet(isPresented: $showJSON) { invalidJSONView(json) } } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index e4f70d9286..1acf08035c 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -130,7 +130,7 @@ struct ChatView: View { } } } - .sheet(item: $selectedMember) { member in + .appSheet(item: $selectedMember) { member in Group { if case let .group(groupInfo) = chat.chatInfo { GroupMemberInfoView(groupInfo: groupInfo, groupMember: member, navigation: true) @@ -138,7 +138,7 @@ struct ChatView: View { } } // it should be presented on top level in order to prevent a bug in SwiftUI on iOS 16 related to .focused() modifier in AddGroupMembersView's search field - .sheet(isPresented: $showAddMembersSheet) { + .appSheet(isPresented: $showAddMembersSheet) { Group { if case let .group(groupInfo) = cInfo { AddGroupMembersView(chat: chat, groupInfo: groupInfo) @@ -221,7 +221,7 @@ struct ChatView: View { } label: { ChatInfoToolbar(chat: chat) } - .sheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) { + .appSheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) { ChatInfoView( chat: chat, contact: contact, @@ -236,7 +236,7 @@ struct ChatView: View { ChatInfoToolbar(chat: chat) .tint(theme.colors.primary) } - .sheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) { + .appSheet(isPresented: $showChatInfoSheet, onDismiss: { theme = buildTheme() }) { GroupChatInfoView( chat: chat, groupInfo: Binding( @@ -301,7 +301,7 @@ struct ChatView: View { if groupInfo.canAddMembers { if (chat.chatInfo.incognito) { groupLinkButton() - .sheet(isPresented: $showGroupLinkSheet) { + .appSheet(isPresented: $showGroupLinkSheet) { GroupLinkView( groupId: groupInfo.groupId, groupLink: $groupLink, diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index 3fec730c65..d2a93b9bd1 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -384,7 +384,7 @@ struct ChatListNavLink: View { .tint(theme.colors.primary) } .frame(height: dynamicRowHeight) - .sheet(isPresented: $showContactConnectionInfo) { + .appSheet(isPresented: $showContactConnectionInfo) { Group { if case let .contactConnection(contactConnection) = chat.chatInfo { ContactConnectionInfo(contactConnection: contactConnection) @@ -464,7 +464,7 @@ struct ChatListNavLink: View { .padding(4) .frame(height: dynamicRowHeight) .onTapGesture { showInvalidJSON = true } - .sheet(isPresented: $showInvalidJSON) { + .appSheet(isPresented: $showInvalidJSON) { invalidJSONView(json) .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) } diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index e513df558e..a1b40aadbe 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -428,7 +428,7 @@ struct SubsStatusIndicator: View { .onDisappear { stopTask() } - .sheet(isPresented: $showServersSummary) { + .appSheet(isPresented: $showServersSummary) { ServersSummaryView() .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) } diff --git a/apps/ios/Shared/Views/Helpers/AppSheet.swift b/apps/ios/Shared/Views/Helpers/AppSheet.swift new file mode 100644 index 0000000000..872304cc9d --- /dev/null +++ b/apps/ios/Shared/Views/Helpers/AppSheet.swift @@ -0,0 +1,53 @@ +// +// AppSheet.swift +// SimpleX (iOS) +// +// Created by Evgeny on 24/11/2022. +// Copyright © 2022 SimpleX Chat. All rights reserved. +// + +import SwiftUI + +class AppSheetState: ObservableObject { + static let shared = AppSheetState() + @Published var biometricAuth: Bool = false + @Published var scenePhaseActive: Bool = false + + func redactionReasons(_ protectScreen: Bool) -> RedactionReasons { + !protectScreen || scenePhaseActive || biometricAuth + ? RedactionReasons() + : RedactionReasons.placeholder + } +} + +private struct PrivacySensitive: ViewModifier { + @AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false + // Screen protection doesn't work for appSheet on iOS 16 if @Environment(\.scenePhase) is used instead of global state + @ObservedObject var appSheetState: AppSheetState = AppSheetState.shared + + func body(content: Content) -> some View { + content.redacted(reason: appSheetState.redactionReasons(protectScreen)) + } +} + +extension View { + func appSheet( + isPresented: Binding, + onDismiss: (() -> Void)? = nil, + content: @escaping () -> Content + ) -> some View where Content: View { + sheet(isPresented: isPresented, onDismiss: onDismiss) { + content().modifier(PrivacySensitive()) + } + } + + func appSheet( + item: Binding, + onDismiss: (() -> Void)? = nil, + content: @escaping (T) -> Content + ) -> some View where T: Identifiable, Content: View { + sheet(item: item, onDismiss: onDismiss) { it in + content(it).modifier(PrivacySensitive()) + } + } +} diff --git a/apps/ios/Shared/Views/Helpers/LocalAuthenticationUtils.swift b/apps/ios/Shared/Views/Helpers/LocalAuthenticationUtils.swift index 7a90a3f833..f7f71d2e83 100644 --- a/apps/ios/Shared/Views/Helpers/LocalAuthenticationUtils.swift +++ b/apps/ios/Shared/Views/Helpers/LocalAuthenticationUtils.swift @@ -63,9 +63,11 @@ func systemAuthenticate(_ reason: String, _ completed: @escaping (LAResult) -> V var authAvailabilityError: NSError? if laContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: &authAvailabilityError) { logger.debug("DEBUGGING: systemAuthenticate: canEvaluatePolicy callback") + AppSheetState.shared.biometricAuth = true laContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) { success, authError in logger.debug("DEBUGGING: systemAuthenticate evaluatePolicy callback") DispatchQueue.main.async { + AppSheetState.shared.biometricAuth = false if success { completed(LAResult.success) } else { diff --git a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift index a57afa077a..3ca3e0e4d8 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift @@ -29,7 +29,7 @@ struct NewChatMenuButton: View { .scaledToFit() .frame(width: 24, height: 24) } - .sheet(isPresented: $showNewChatSheet) { + .appSheet(isPresented: $showNewChatSheet) { NewChatSheet(pendingConnection: $pendingConnection) .environment(\EnvironmentValues.refresh as! WritableKeyPath, nil) .onDisappear { diff --git a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift index d75200462c..330ce56e0b 100644 --- a/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift +++ b/apps/ios/Shared/Views/UserSettings/UserProfilesView.swift @@ -124,7 +124,7 @@ struct UserProfilesView: View { deleteModeButton("Profile and server connections", true) deleteModeButton("Local profile data only", false) } - .sheet(item: $selectedUser) { user in + .appSheet(item: $selectedUser) { user in HiddenProfileView(user: user, profileHidden: $profileHidden) } .onChange(of: profileHidden) { _ in @@ -132,7 +132,7 @@ struct UserProfilesView: View { withAnimation { profileHidden = false } } } - .sheet(item: $profileAction) { action in + .appSheet(item: $profileAction) { action in profileActionView(action) } .alert(item: $alert) { alert in diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index f11076f7ed..2903388fb9 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -81,6 +81,7 @@ 5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C3279559F40002BEB4 /* SimpleXApp.swift */; }; 5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C4279559F40002BEB4 /* ContentView.swift */; }; 5CA059EF279559F40002BEB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5CA059C5279559F40002BEB4 /* Assets.xcassets */; }; + 5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA7DFC229302AF000F7FDDE /* AppSheet.swift */; }; 5CADE79A29211BB900072E13 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CADE79929211BB900072E13 /* PreferencesView.swift */; }; 5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CADE79B292131E900072E13 /* ContactPreferencesView.swift */; }; 5CB0BA882826CB3A00B3292C /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CB0BA862826CB3A00B3292C /* InfoPlist.strings */; }; @@ -410,6 +411,7 @@ 5CA3ED4D2A942170005D71E2 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Localizable.strings; sourceTree = ""; }; 5CA3ED4F2A9422D1005D71E2 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = "th.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5CA3ED502A9422D1005D71E2 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = ""; }; + 5CA7DFC229302AF000F7FDDE /* AppSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSheet.swift; sourceTree = ""; }; 5CA85D0A297218AA0095AF72 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; 5CA85D0C297219EF0095AF72 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = "it.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = ""; }; 5CA85D0D297219EF0095AF72 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -786,6 +788,7 @@ 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */, 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */, 5C6BA666289BD954009B8ECC /* DismissSheets.swift */, + 5CA7DFC229302AF000F7FDDE /* AppSheet.swift */, 18415A7F0F189D87DEFEABCA /* PressedButtonStyle.swift */, 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */, 18415DAAAD1ADBEDB0EDA852 /* VideoPlayerView.swift */, @@ -1427,6 +1430,7 @@ 8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */, 5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */, 5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */, + 5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */, 64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */, 5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */, 5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */,