diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index bb5ab76aaa..e1e3b808b7 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -12,21 +12,22 @@ struct ContentView: View { @ObservedObject var alertManager = AlertManager.shared @ObservedObject var callController = CallController.shared @Binding var doAuthenticate: Bool - @Binding var enteredBackground: Double? - @State private var userAuthorized: Bool? - @State private var laFailed: Bool = false + @Binding var userAuthorized: Bool? + @State private var showChatInfo: Bool = false // TODO comprehensively close modal views on authentication @AppStorage(DEFAULT_SHOW_LA_NOTICE) private var prefShowLANotice = false @AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false var body: some View { ZStack { - if userAuthorized == true { + if prefPerformLA && userAuthorized != true { + Button(action: runAuthenticate) { Label("Unlock", systemImage: "lock") } + } else { if let step = chatModel.onboardingStage { if case .onboardingComplete = step, - let user = chatModel.currentUser { + chatModel.currentUser != nil { ZStack(alignment: .top) { - ChatListView(user: user) + ChatListView(showChatInfo: $showChatInfo) .onAppear { NtfManager.shared.requestAuthorization(onDeny: { alertManager.showAlert(notificationAlert()) @@ -47,54 +48,39 @@ struct ContentView: View { OnboardingView(onboarding: step) } } - } else if prefPerformLA && laFailed { - retryAuthView() - } - } - .onChange(of: doAuthenticate) { doAuth in - if doAuth, authenticationExpired() { - runAuthenticate() } } + .onAppear { if doAuthenticate { runAuthenticate() } } + .onChange(of: doAuthenticate) { _ in if doAuthenticate { runAuthenticate() } } .alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! } } - private func retryAuthView() -> some View { - Button { - laFailed = false - runAuthenticate() - } label: { Label("Retry", systemImage: "arrow.counterclockwise") } - } - private func runAuthenticate() { if !prefPerformLA { userAuthorized = true - } else { - chatModel.showChatInfo = false - DispatchQueue.main.async() { - userAuthorized = false - authenticate(reason: NSLocalizedString("Unlock", comment: "authentication reason")) { laResult in - switch (laResult) { - case .success: - userAuthorized = true - case .failed: - laFailed = true - AlertManager.shared.showAlert(laFailedAlert()) - case .unavailable: - userAuthorized = true - prefPerformLA = false - AlertManager.shared.showAlert(laUnavailableTurningOffAlert()) - } - } + } else if showChatInfo { + showChatInfo = false + DispatchQueue.main.async { + justAuthenticate() } + } else { + justAuthenticate() } } - private func authenticationExpired() -> Bool { - if let enteredBackground = enteredBackground { - return ProcessInfo.processInfo.systemUptime - enteredBackground >= 30 - } else { - return true + private func justAuthenticate() { + userAuthorized = false + authenticate(reason: NSLocalizedString("Unlock", comment: "authentication reason")) { laResult in + switch (laResult) { + case .success: + userAuthorized = true + case .failed: + AlertManager.shared.showAlert(laFailedAlert()) + case .unavailable: + userAuthorized = true + prefPerformLA = false + AlertManager.shared.showAlert(laUnavailableTurningOffAlert()) + } } } diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index f2a30ffd03..0696b0b6e9 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -15,7 +15,6 @@ import SimpleXChat final class ChatModel: ObservableObject { @Published var onboardingStage: OnboardingStage? @Published var currentUser: User? - @Published var showChatInfo: Bool = false // TODO comprehensively close modal views on authentication // list of chat "previews" @Published var chats: [Chat] = [] // current chat diff --git a/apps/ios/Shared/SimpleXApp.swift b/apps/ios/Shared/SimpleXApp.swift index 2c2540eabd..8ce48b9da9 100644 --- a/apps/ios/Shared/SimpleXApp.swift +++ b/apps/ios/Shared/SimpleXApp.swift @@ -25,8 +25,8 @@ struct SimpleXApp: App { @ObservedObject var alertManager = AlertManager.shared @Environment(\.scenePhase) var scenePhase @AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false - @State private var userAuthorized: Bool? = nil - @State private var doAuthenticate: Bool = false + @State private var userAuthorized: Bool? + @State private var doAuthenticate = false @State private var enteredBackground: Double? = nil init() { @@ -39,7 +39,7 @@ struct SimpleXApp: App { var body: some Scene { return WindowGroup { - ContentView(doAuthenticate: $doAuthenticate, enteredBackground: $enteredBackground) + ContentView(doAuthenticate: $doAuthenticate, userAuthorized: $userAuthorized) .environmentObject(chatModel) .onOpenURL { url in logger.debug("ContentView.onOpenURL: \(url)") @@ -56,11 +56,13 @@ struct SimpleXApp: App { switch (phase) { case .background: BGManager.shared.schedule() + if userAuthorized == true { + enteredBackground = ProcessInfo.processInfo.systemUptime + } doAuthenticate = false - enteredBackground = ProcessInfo.processInfo.systemUptime machMessenger.stop() case .active: - doAuthenticate = true + doAuthenticate = authenticationExpired() machMessenger.start() default: break @@ -68,4 +70,12 @@ struct SimpleXApp: App { } } } + + private func authenticationExpired() -> Bool { + if let enteredBackground = enteredBackground { + return ProcessInfo.processInfo.systemUptime - enteredBackground >= 30 + } else { + return true + } + } } diff --git a/apps/ios/Shared/Views/Chat/ChatInfoView.swift b/apps/ios/Shared/Views/Chat/ChatInfoView.swift index 7bd8f62836..fc82d543b6 100644 --- a/apps/ios/Shared/Views/Chat/ChatInfoView.swift +++ b/apps/ios/Shared/Views/Chat/ChatInfoView.swift @@ -13,6 +13,7 @@ struct ChatInfoView: View { @EnvironmentObject var chatModel: ChatModel @ObservedObject var alertManager = AlertManager.shared @ObservedObject var chat: Chat + @Binding var showChatInfo: Bool @State var alert: ChatInfoViewAlert? = nil @State var deletingContact: Contact? @@ -99,7 +100,7 @@ struct ChatInfoView: View { try await apiDeleteChat(type: .direct, id: contact.apiId) DispatchQueue.main.async { chatModel.removeChat(contact.id) - chatModel.showChatInfo = false + showChatInfo = false } } catch let error { logger.error("ChatInfoView.deleteContactAlert apiDeleteChat error: \(error.localizedDescription)") @@ -118,7 +119,7 @@ struct ChatInfoView: View { Task { await clearChat(chat) DispatchQueue.main.async { - chatModel.showChatInfo = false + showChatInfo = false } } }, @@ -130,6 +131,6 @@ struct ChatInfoView: View { struct ChatInfoView_Previews: PreviewProvider { static var previews: some View { @State var showChatInfo = true - return ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])) + return ChatInfoView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), showChatInfo: $showChatInfo) } } diff --git a/apps/ios/Shared/Views/Chat/ChatView.swift b/apps/ios/Shared/Views/Chat/ChatView.swift index 54cc675934..79b30ebf27 100644 --- a/apps/ios/Shared/Views/Chat/ChatView.swift +++ b/apps/ios/Shared/Views/Chat/ChatView.swift @@ -16,6 +16,7 @@ struct ChatView: View { @Environment(\.colorScheme) var colorScheme @AppStorage(DEFAULT_EXPERIMENTAL_CALLS) private var enableCalls = false @ObservedObject var chat: Chat + @Binding var showChatInfo: Bool @State private var composeState = ComposeState() @State private var deletingItem: ChatItem? = nil @FocusState private var keyboardVisible: Bool @@ -98,12 +99,12 @@ struct ChatView: View { } ToolbarItem(placement: .principal) { Button { - chatModel.showChatInfo = true + showChatInfo = true } label: { ChatInfoToolbar(chat: chat) } - .sheet(isPresented: $chatModel.showChatInfo) { - ChatInfoView(chat: chat) + .sheet(isPresented: $showChatInfo) { + ChatInfoView(chat: chat, showChatInfo: $showChatInfo) } } ToolbarItem(placement: .navigationBarTrailing) { @@ -270,7 +271,8 @@ struct ChatView_Previews: PreviewProvider { ChatItem.getSample(8, .directSnd, .now, "👍👍👍👍"), ChatItem.getSample(9, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.") ] - return ChatView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])) + @State var showChatInfo = false + return ChatView(chat: Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []), showChatInfo: $showChatInfo) .environmentObject(chatModel) } } diff --git a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift index c04fdeb3ba..bce1f0a662 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListNavLink.swift @@ -12,6 +12,7 @@ import SimpleXChat struct ChatListNavLink: View { @EnvironmentObject var chatModel: ChatModel @State var chat: Chat + @Binding var showChatInfo: Bool @State private var showContactRequestDialog = false var body: some View { @@ -28,7 +29,7 @@ struct ChatListNavLink: View { } private func chatView() -> some View { - ChatView(chat: chat) + ChatView(chat: chat, showChatInfo: $showChatInfo) .onAppear { do { let cInfo = chat.chatInfo @@ -279,19 +280,20 @@ struct ChatListNavLink: View { struct ChatListNavLink_Previews: PreviewProvider { static var previews: some View { @State var chatId: String? = "@1" + @State var showChatInfo = false return Group { ChatListNavLink(chat: Chat( chatInfo: ChatInfo.sampleData.direct, chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")] - )) + ), showChatInfo: $showChatInfo) ChatListNavLink(chat: Chat( chatInfo: ChatInfo.sampleData.direct, chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")] - )) + ), showChatInfo: $showChatInfo) ChatListNavLink(chat: Chat( chatInfo: ChatInfo.sampleData.contactRequest, chatItems: [] - )) + ), showChatInfo: $showChatInfo) } .previewLayout(.fixed(width: 360, height: 80)) } diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index f0674af969..721365c2f1 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -11,18 +11,17 @@ import SimpleXChat struct ChatListView: View { @EnvironmentObject var chatModel: ChatModel + @Binding var showChatInfo: Bool // not really used in this view @State private var showSettings = false @State private var searchText = "" @AppStorage(DEFAULT_PENDING_CONNECTIONS) private var pendingConnections = true - var user: User - var body: some View { let v = NavigationView { List { ForEach(filteredChats()) { chat in - ChatListNavLink(chat: chat) + ChatListNavLink(chat: chat, showChatInfo: $showChatInfo) .padding(.trailing, -16) } } @@ -93,10 +92,11 @@ struct ChatListView_Previews: PreviewProvider { ) ] + @State var showChatInfo = false return Group { - ChatListView(user: User.sampleData) + ChatListView(showChatInfo: $showChatInfo) .environmentObject(chatModel) - ChatListView(user: User.sampleData) + ChatListView(showChatInfo: $showChatInfo) .environmentObject(ChatModel()) } } diff --git a/apps/ios/SimpleX.xcodeproj/project.pbxproj b/apps/ios/SimpleX.xcodeproj/project.pbxproj index a0d4de258b..0d41029ff4 100644 --- a/apps/ios/SimpleX.xcodeproj/project.pbxproj +++ b/apps/ios/SimpleX.xcodeproj/project.pbxproj @@ -1070,7 +1070,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 53; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; @@ -1093,7 +1093,7 @@ ); "LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Libraries/ios"; "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Libraries/sim"; - MARKETING_VERSION = 2.2; + MARKETING_VERSION = 2.2.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; @@ -1113,7 +1113,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 53; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; ENABLE_PREVIEWS = YES; @@ -1136,7 +1136,7 @@ ); "LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = "$(PROJECT_DIR)/Libraries/ios"; "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]" = "$(PROJECT_DIR)/Libraries/sim"; - MARKETING_VERSION = 2.2; + MARKETING_VERSION = 2.2.1; PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app; PRODUCT_NAME = SimpleX; SDKROOT = iphoneos; @@ -1194,7 +1194,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 53; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; @@ -1215,7 +1215,7 @@ "$(inherited)", "$(PROJECT_DIR)/Libraries/sim", ); - MARKETING_VERSION = 2.2; + MARKETING_VERSION = 2.2.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -1233,7 +1233,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 53; DEVELOPMENT_TEAM = 5NN7GUYB6T; ENABLE_BITCODE = NO; GENERATE_INFOPLIST_FILE = YES; @@ -1254,7 +1254,7 @@ "$(inherited)", "$(PROJECT_DIR)/Libraries/sim", ); - MARKETING_VERSION = 2.2; + MARKETING_VERSION = 2.2.1; PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos;