2022-01-21 11:09:33 +00:00
|
|
|
//
|
|
|
|
// SimpleXApp.swift
|
|
|
|
// Shared
|
|
|
|
//
|
|
|
|
// Created by Evgeny Poberezkin on 17/01/2022.
|
|
|
|
//
|
|
|
|
|
|
|
|
import SwiftUI
|
2022-02-09 22:53:06 +00:00
|
|
|
import OSLog
|
2022-05-31 07:55:13 +01:00
|
|
|
import SimpleXChat
|
2022-02-09 22:53:06 +00:00
|
|
|
|
|
|
|
let logger = Logger()
|
2022-01-21 11:09:33 +00:00
|
|
|
|
|
|
|
@main
|
|
|
|
struct SimpleXApp: App {
|
2022-04-21 20:04:22 +01:00
|
|
|
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
2022-02-09 22:53:06 +00:00
|
|
|
@StateObject private var chatModel = ChatModel.shared
|
2022-05-28 14:58:52 +04:00
|
|
|
@ObservedObject var alertManager = AlertManager.shared
|
2023-12-18 22:04:49 +04:00
|
|
|
|
2022-02-09 22:53:06 +00:00
|
|
|
@Environment(\.scenePhase) var scenePhase
|
2023-12-18 22:04:49 +04:00
|
|
|
@State private var enteredBackgroundAuthenticated: TimeInterval? = nil
|
2025-04-14 21:25:32 +01:00
|
|
|
@State private var appOpenUrlLater: URL?
|
2022-02-09 22:53:06 +00:00
|
|
|
|
2022-01-22 17:54:22 +00:00
|
|
|
init() {
|
2023-12-23 13:06:59 +00:00
|
|
|
DispatchQueue.global(qos: .background).sync {
|
|
|
|
haskell_init()
|
2023-11-22 22:12:42 +00:00
|
|
|
// hs_init(0, nil)
|
2023-12-23 13:06:59 +00:00
|
|
|
}
|
2022-05-27 16:36:33 +01:00
|
|
|
UserDefaults.standard.register(defaults: appDefaults)
|
2022-07-10 14:28:00 +01:00
|
|
|
setGroupDefaults()
|
2022-08-03 12:36:51 +01:00
|
|
|
registerGroupDefaults()
|
2022-06-24 13:52:20 +01:00
|
|
|
setDbContainer()
|
2022-02-09 22:53:06 +00:00
|
|
|
BGManager.shared.register()
|
|
|
|
NtfManager.shared.registerCategories()
|
2022-01-22 17:54:22 +00:00
|
|
|
}
|
2022-01-29 11:10:04 +00:00
|
|
|
|
2022-01-21 11:09:33 +00:00
|
|
|
var body: some Scene {
|
2023-12-18 22:04:49 +04:00
|
|
|
WindowGroup {
|
|
|
|
// contentAccessAuthenticationExtended has to be passed to ContentView on view initialization,
|
|
|
|
// so that it's computed by the time view renders, and not on event after rendering
|
|
|
|
ContentView(contentAccessAuthenticationExtended: !authenticationExpired())
|
2022-01-29 11:10:04 +00:00
|
|
|
.environmentObject(chatModel)
|
2024-07-03 22:42:13 +01:00
|
|
|
.environmentObject(AppTheme.shared)
|
2022-02-01 20:30:33 +00:00
|
|
|
.onOpenURL { url in
|
2022-02-09 22:53:06 +00:00
|
|
|
logger.debug("ContentView.onOpenURL: \(url)")
|
2025-04-14 21:25:32 +01:00
|
|
|
if AppChatState.shared.value == .active {
|
|
|
|
chatModel.appOpenUrl = url
|
|
|
|
} else {
|
|
|
|
appOpenUrlLater = url
|
|
|
|
}
|
2022-02-01 20:30:33 +00:00
|
|
|
}
|
2022-01-29 11:10:04 +00:00
|
|
|
.onAppear() {
|
2024-03-11 21:17:28 +07:00
|
|
|
// Present screen for continue migration if it wasn't finished yet
|
|
|
|
if chatModel.migrationState != nil {
|
|
|
|
// It's important, otherwise, user may be locked in undefined state
|
|
|
|
onboardingStageDefault.set(.step1_SimpleXInfo)
|
|
|
|
chatModel.onboardingStage = onboardingStageDefault.get()
|
|
|
|
} else if kcAppPassword.get() == nil || kcSelfDestructPassword.get() == nil {
|
2024-01-10 04:01:41 +07:00
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
|
|
|
initChatAndMigrate()
|
|
|
|
}
|
2023-05-22 15:49:46 +02:00
|
|
|
}
|
2022-02-09 22:53:06 +00:00
|
|
|
}
|
|
|
|
.onChange(of: scenePhase) { phase in
|
2023-03-14 11:12:40 +03:00
|
|
|
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
|
2024-08-09 14:43:42 +04:00
|
|
|
AppSheetState.shared.scenePhaseActive = phase == .active
|
2022-05-27 18:21:35 +04:00
|
|
|
switch (phase) {
|
|
|
|
case .background:
|
2023-12-18 22:04:49 +04:00
|
|
|
// --- authentication
|
|
|
|
// see ContentView .onChange(of: scenePhase) for remaining authentication logic
|
|
|
|
if chatModel.contentViewAccessAuthenticated {
|
|
|
|
enteredBackgroundAuthenticated = ProcessInfo.processInfo.systemUptime
|
|
|
|
}
|
|
|
|
chatModel.contentViewAccessAuthenticated = false
|
|
|
|
// authentication ---
|
|
|
|
|
2023-03-14 11:12:40 +03:00
|
|
|
if CallController.useCallKit() && chatModel.activeCall != nil {
|
2023-03-16 19:57:43 +00:00
|
|
|
CallController.shared.shouldSuspendChat = true
|
2023-03-14 11:12:40 +03:00
|
|
|
} else {
|
|
|
|
suspendChat()
|
|
|
|
BGManager.shared.schedule()
|
|
|
|
}
|
2023-01-23 13:20:58 +00:00
|
|
|
NtfManager.shared.setNtfBadgeCount(chatModel.totalUnreadCountForAllUsers())
|
2022-05-27 18:21:35 +04:00
|
|
|
case .active:
|
2023-03-16 19:57:43 +00:00
|
|
|
CallController.shared.shouldSuspendChat = false
|
2023-12-11 12:34:56 +00:00
|
|
|
let appState = AppChatState.shared.value
|
2023-12-18 22:04:49 +04:00
|
|
|
|
2023-12-11 12:59:49 +00:00
|
|
|
if appState != .stopped {
|
|
|
|
startChatAndActivate {
|
2024-10-18 18:07:38 +04:00
|
|
|
if chatModel.chatRunning == true {
|
|
|
|
if let ntfResponse = chatModel.notificationResponse {
|
|
|
|
chatModel.notificationResponse = nil
|
|
|
|
NtfManager.shared.processNotificationResponse(ntfResponse)
|
|
|
|
}
|
|
|
|
if appState.inactive {
|
|
|
|
Task {
|
|
|
|
await updateChats()
|
|
|
|
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
|
|
|
await updateCallInvitations()
|
|
|
|
}
|
2025-04-14 21:25:32 +01:00
|
|
|
if let url = appOpenUrlLater {
|
|
|
|
await MainActor.run {
|
|
|
|
appOpenUrlLater = nil
|
|
|
|
chatModel.appOpenUrl = url
|
|
|
|
}
|
|
|
|
}
|
2024-08-21 21:16:57 +03:00
|
|
|
}
|
2025-04-14 21:25:32 +01:00
|
|
|
} else if let url = appOpenUrlLater {
|
|
|
|
appOpenUrlLater = nil
|
|
|
|
chatModel.appOpenUrl = url
|
2023-12-11 12:59:49 +00:00
|
|
|
}
|
2023-12-09 21:59:40 +00:00
|
|
|
}
|
2023-03-15 13:21:21 +03:00
|
|
|
}
|
2022-06-27 10:28:30 +01:00
|
|
|
}
|
2022-05-27 18:21:35 +04:00
|
|
|
default:
|
|
|
|
break
|
2022-02-06 18:26:22 +00:00
|
|
|
}
|
2022-01-29 11:10:04 +00:00
|
|
|
}
|
2022-01-21 11:09:33 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-03 09:16:07 +01:00
|
|
|
|
2022-06-24 13:52:20 +01:00
|
|
|
private func setDbContainer() {
|
|
|
|
// Uncomment and run once to open DB in app documents folder:
|
2022-07-01 09:49:30 +01:00
|
|
|
// dbContainerGroupDefault.set(.documents)
|
|
|
|
// v3DBMigrationDefault.set(.offer)
|
2022-06-24 13:52:20 +01:00
|
|
|
// to create database in app documents folder also uncomment:
|
2022-07-01 20:33:20 +01:00
|
|
|
// let legacyDatabase = true
|
2022-06-24 13:52:20 +01:00
|
|
|
let legacyDatabase = hasLegacyDatabase()
|
|
|
|
if legacyDatabase, case .documents = dbContainerGroupDefault.get() {
|
|
|
|
dbContainerGroupDefault.set(.documents)
|
2022-07-03 19:53:07 +01:00
|
|
|
setMigrationState(.offer)
|
2024-01-08 10:56:01 +00:00
|
|
|
logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath())*.db")
|
2022-06-24 13:52:20 +01:00
|
|
|
} else {
|
|
|
|
dbContainerGroupDefault.set(.group)
|
2022-07-03 19:53:07 +01:00
|
|
|
setMigrationState(.ready)
|
2024-01-08 10:56:01 +00:00
|
|
|
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath())*.db")
|
|
|
|
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not") present")
|
2022-06-24 13:52:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-03 19:53:07 +01:00
|
|
|
private func setMigrationState(_ state: V3DBMigrationState) {
|
|
|
|
if case .migrated = v3DBMigrationDefault.get() { return }
|
|
|
|
v3DBMigrationDefault.set(state)
|
|
|
|
}
|
|
|
|
|
2022-06-03 09:16:07 +01:00
|
|
|
private func authenticationExpired() -> Bool {
|
2023-12-18 22:04:49 +04:00
|
|
|
if let enteredBackgroundAuthenticated = enteredBackgroundAuthenticated {
|
2023-04-12 12:22:55 +02:00
|
|
|
let delay = Double(UserDefaults.standard.integer(forKey: DEFAULT_LA_LOCK_DELAY))
|
2023-12-18 22:04:49 +04:00
|
|
|
return ProcessInfo.processInfo.systemUptime - enteredBackgroundAuthenticated >= delay
|
2022-06-03 09:16:07 +01:00
|
|
|
} else {
|
|
|
|
return true
|
|
|
|
}
|
2023-03-15 18:32:27 +03:00
|
|
|
}
|
|
|
|
|
2024-08-21 21:16:57 +03:00
|
|
|
private func updateChats() async {
|
2022-07-02 17:18:45 +01:00
|
|
|
do {
|
2024-08-21 21:16:57 +03:00
|
|
|
let chats = try await apiGetChatsAsync()
|
|
|
|
await MainActor.run { chatModel.updateChats(chats) }
|
2022-07-02 17:18:45 +01:00
|
|
|
if let id = chatModel.chatId,
|
2024-12-05 16:15:24 +00:00
|
|
|
let chat = chatModel.getChat(id),
|
|
|
|
!NtfManager.shared.navigatingToChat {
|
2024-08-14 22:14:40 +03:00
|
|
|
Task { await loadChat(chat: chat, clearItems: false) }
|
2022-07-02 17:18:45 +01:00
|
|
|
}
|
2023-08-08 17:26:56 +04:00
|
|
|
if let ncr = chatModel.ntfContactRequest {
|
2024-08-21 21:16:57 +03:00
|
|
|
await MainActor.run { chatModel.ntfContactRequest = nil }
|
2023-08-08 17:26:56 +04:00
|
|
|
if case let .contactRequest(contactRequest) = chatModel.getChat(ncr.chatId)?.chatInfo {
|
|
|
|
Task { await acceptContactRequest(incognito: ncr.incognito, contactRequest: contactRequest) }
|
2022-07-22 08:10:37 +01:00
|
|
|
}
|
|
|
|
}
|
2022-07-02 17:18:45 +01:00
|
|
|
} catch let error {
|
|
|
|
logger.error("apiGetChats: cannot update chats \(responseError(error))")
|
|
|
|
}
|
|
|
|
}
|
2022-07-05 15:15:15 +04:00
|
|
|
|
2024-08-21 21:16:57 +03:00
|
|
|
private func updateCallInvitations() async {
|
2022-07-05 15:15:15 +04:00
|
|
|
do {
|
2024-08-21 21:16:57 +03:00
|
|
|
try await refreshCallInvitations()
|
2022-07-22 08:10:37 +01:00
|
|
|
} catch let error {
|
2022-07-05 15:15:15 +04:00
|
|
|
logger.error("apiGetCallInvitations: cannot update call invitations \(responseError(error))")
|
|
|
|
}
|
|
|
|
}
|
2022-01-21 11:09:33 +00:00
|
|
|
}
|