diff --git a/apps/ios/Shared/AppDelegate.swift b/apps/ios/Shared/AppDelegate.swift index bbec769a79..64f963e973 100644 --- a/apps/ios/Shared/AppDelegate.swift +++ b/apps/ios/Shared/AppDelegate.swift @@ -65,11 +65,13 @@ class AppDelegate: NSObject, UIApplicationDelegate { logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessages") // TODO remove // NtfManager.shared.notifyCheckingMessages() - receiveMessages(completionHandler) + completionHandler(.noData) +// receiveMessages(completionHandler) } else if let smpQueue = ntfData["checkMessage"] as? String { // TODO check if app in background logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessage \(smpQueue)") - receiveMessages(completionHandler) + completionHandler(.noData) +// receiveMessages(completionHandler) } else { completionHandler(.noData) } diff --git a/apps/ios/Shared/ContentView.swift b/apps/ios/Shared/ContentView.swift index 356292334b..914071e417 100644 --- a/apps/ios/Shared/ContentView.swift +++ b/apps/ios/Shared/ContentView.swift @@ -14,7 +14,6 @@ struct ContentView: View { @Binding var doAuthenticate: Bool @Binding var userAuthorized: Bool? @State private var showChatInfo: Bool = false // TODO comprehensively close modal views on authentication - @State private var v3DBMigration = v3DBMigrationDefault.get() @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 @@ -23,16 +22,14 @@ struct ContentView: View { ZStack { if prefPerformLA && userAuthorized != true { Button(action: runAuthenticate) { Label("Unlock", systemImage: "lock") } - } else { - if let step = chatModel.onboardingStage { - if case .onboardingComplete = step, - chatModel.currentUser != nil { - mainView() - } else { - OnboardingView(onboarding: step) - } - } else if !v3DBMigrationDefault.get().startChat { - MigrateToAppGroupView() + } else if !chatModel.v3DBMigration.startChat { + MigrateToAppGroupView() + } else if let step = chatModel.onboardingStage { + if case .onboardingComplete = step, + chatModel.currentUser != nil { + mainView() + } else { + OnboardingView(onboarding: step) } } } diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 41ba73c613..2292a60c94 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -14,6 +14,7 @@ import SimpleXChat final class ChatModel: ObservableObject { @Published var onboardingStage: OnboardingStage? + @Published var v3DBMigration: V3DBMigrationState = v3DBMigrationDefault.get() @Published var currentUser: User? @Published var chatRunning: Bool? @Published var chatDbChanged = false @@ -92,13 +93,21 @@ final class ChatModel: ObservableObject { func replaceChat(_ id: String, _ chat: Chat) { if let i = getChatIndex(id) { + let serverInfo = chats[i].serverInfo chats[i] = chat + chats[i].serverInfo = serverInfo } else { // invalid state, correcting chats.insert(chat, at: 0) } } + func replaceChats(with newChats: [Chat]) { + for chat in newChats { + replaceChat(chat.id, chat) + } + } + func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) { // update previews if let i = getChatIndex(cInfo.id) { @@ -248,6 +257,7 @@ final class Chat: ObservableObject, Identifiable { @Published var chatItems: [ChatItem] @Published var chatStats: ChatStats @Published var serverInfo = ServerInfo(networkStatus: .unknown) + var created = Date.now struct ServerInfo: Decodable { var networkStatus: NetworkStatus @@ -305,4 +315,6 @@ final class Chat: ObservableObject, Identifiable { } var id: ChatId { get { chatInfo.id } } + + var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } } } diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index 5d2b9ade8a..fc2a782290 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -520,7 +520,6 @@ func initializeChat(start: Bool) throws { func startChat() throws { logger.debug("startChat") let m = ChatModel.shared - // TODO set file folder once, before chat is started let justStarted = try apiStartChat() if justStarted { m.userAddress = try apiGetUserAddress() diff --git a/apps/ios/Shared/SimpleXApp.swift b/apps/ios/Shared/SimpleXApp.swift index 3ef7993e84..a79f7bab83 100644 --- a/apps/ios/Shared/SimpleXApp.swift +++ b/apps/ios/Shared/SimpleXApp.swift @@ -40,7 +40,8 @@ struct SimpleXApp: App { } .onAppear() { do { - try initializeChat(start: v3DBMigrationDefault.get().startChat) + chatModel.v3DBMigration = v3DBMigrationDefault.get() + try initializeChat(start: chatModel.v3DBMigration.startChat) } catch let error { fatalError("Failed to start or load chats: \(responseError(error))") } @@ -59,11 +60,15 @@ struct SimpleXApp: App { } doAuthenticate = false case .active: - activateChat() if chatModel.chatRunning == true { ChatReceiver.shared.start() + } + let appState = appStateGroupDefault.get() + activateChat() + if appState.inactive && chatModel.chatRunning == true { do { - chatModel.chats = try apiGetChats() + let chats = try apiGetChats() + chatModel.replaceChats(with: chats) } catch let error { logger.error("apiGetChats: cannot update chats \(responseError(error))") } diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index e4b3d87b18..e3087ed30a 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -20,7 +20,7 @@ struct ChatListView: View { var body: some View { let v = NavigationView { List { - ForEach(filteredChats()) { chat in + ForEach(filteredChats(), id: \.viewId) { chat in ChatListNavLink(chat: chat, showChatInfo: $showChatInfo) .padding(.trailing, -16) .disabled(chatModel.chatRunning != true) diff --git a/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift b/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift index 3749047b3f..a53c6a2b40 100644 --- a/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift +++ b/apps/ios/Shared/Views/Database/MigrateToAppGroupView.swift @@ -37,7 +37,6 @@ let v3DBMigrationDefault = EnumDefault( struct MigrateToAppGroupView: View { @EnvironmentObject var chatModel: ChatModel - @State private var v3DBMigration = v3DBMigrationDefault.get() @State private var migrationError = "" @AppStorage(DEFAULT_CHAT_ARCHIVE_NAME) private var chatArchiveName: String? @AppStorage(DEFAULT_CHAT_ARCHIVE_TIME) private var chatArchiveTime: Double = 0 @@ -46,7 +45,7 @@ struct MigrateToAppGroupView: View { ZStack(alignment: .topLeading) { Text("Database migration").font(.largeTitle) - switch v3DBMigration { + switch chatModel.v3DBMigration { case .offer: VStack(alignment: .leading, spacing: 16) { Text("To support instant push notifications the chat database has to be migrated.") @@ -128,7 +127,7 @@ struct MigrateToAppGroupView: View { default: Spacer() Text("Unexpected migration state") - Text("\(v3DBMigration.rawValue)") + Text("\(chatModel.v3DBMigration.rawValue)") Spacer() skipMigration() } @@ -174,7 +173,7 @@ struct MigrateToAppGroupView: View { } private func setV3DBMigration(_ value: V3DBMigrationState) { - v3DBMigration = value + chatModel.v3DBMigration = value v3DBMigrationDefault.set(value) } @@ -201,7 +200,7 @@ struct MigrateToAppGroupView: View { await MainActor.run { setV3DBMigration(.migrating) } dbContainerGroupDefault.set(.group) resetChatCtrl() - try initializeChat(start: false) + try await MainActor.run { try initializeChat(start: false) } try await apiImportArchive(config: config) await MainActor.run { setV3DBMigration(.migrated) } } catch let error { diff --git a/apps/ios/SimpleXChat/AppGroup.swift b/apps/ios/SimpleXChat/AppGroup.swift index b55c559bcd..b71d192969 100644 --- a/apps/ios/SimpleXChat/AppGroup.swift +++ b/apps/ios/SimpleXChat/AppGroup.swift @@ -22,6 +22,14 @@ public enum AppState: String { case bgRefresh case suspending case suspended + + public var inactive: Bool { + switch self { + case .suspending: return true + case .suspended: return true + default: return false + } + } } public enum DBContainer: String {