mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-01 03:05:55 +00:00
* ios: migration via link * changes in UI * UI * UI and API changes * UI and logic * simplify statement * UI, API, logic * formatting * animation fix * better animation * test * changed directory * changes * migrating to device * migrate settings * more state updates on main thread * texts * continue migration after restart * toggle for saving passphrase and footer text * no visual arthefacts when deleting a chat after migration * saving settings before changing passphrase * back button is looking disabled when it's disabled * fixed starting chat issues when migrating to device * paste and share link elements * proper import process and refactoring UI in SimpleXInfo * show progress on settings while starting chat * title bold font * changes as in Android * brace * changes as in Android * rename to prevent confusion * fixes and adapted to Android * unused param * comment * don't allow going back on Archiving step * update core library * changes as in Android * correction * correction * change * qr code * update network settings view * update progress * changes * navigation view and focus in text field * texts --------- Co-authored-by: Avently <avently@local> Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com> Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
158 lines
6.4 KiB
Swift
158 lines
6.4 KiB
Swift
//
|
|
// SimpleXApp.swift
|
|
// Shared
|
|
//
|
|
// Created by Evgeny Poberezkin on 17/01/2022.
|
|
//
|
|
|
|
import SwiftUI
|
|
import OSLog
|
|
import SimpleXChat
|
|
|
|
let logger = Logger()
|
|
|
|
@main
|
|
struct SimpleXApp: App {
|
|
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
|
@StateObject private var chatModel = ChatModel.shared
|
|
@ObservedObject var alertManager = AlertManager.shared
|
|
|
|
@Environment(\.scenePhase) var scenePhase
|
|
@State private var enteredBackgroundAuthenticated: TimeInterval? = nil
|
|
|
|
init() {
|
|
DispatchQueue.global(qos: .background).sync {
|
|
haskell_init()
|
|
// hs_init(0, nil)
|
|
}
|
|
UserDefaults.standard.register(defaults: appDefaults)
|
|
setGroupDefaults()
|
|
registerGroupDefaults()
|
|
setDbContainer()
|
|
BGManager.shared.register()
|
|
NtfManager.shared.registerCategories()
|
|
}
|
|
|
|
var body: some Scene {
|
|
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())
|
|
.environmentObject(chatModel)
|
|
.onOpenURL { url in
|
|
logger.debug("ContentView.onOpenURL: \(url)")
|
|
chatModel.appOpenUrl = url
|
|
}
|
|
.onAppear() {
|
|
// 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 {
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
|
initChatAndMigrate()
|
|
}
|
|
}
|
|
}
|
|
.onChange(of: scenePhase) { phase in
|
|
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
|
|
switch (phase) {
|
|
case .background:
|
|
// --- authentication
|
|
// see ContentView .onChange(of: scenePhase) for remaining authentication logic
|
|
if chatModel.contentViewAccessAuthenticated {
|
|
enteredBackgroundAuthenticated = ProcessInfo.processInfo.systemUptime
|
|
}
|
|
chatModel.contentViewAccessAuthenticated = false
|
|
// authentication ---
|
|
|
|
if CallController.useCallKit() && chatModel.activeCall != nil {
|
|
CallController.shared.shouldSuspendChat = true
|
|
} else {
|
|
suspendChat()
|
|
BGManager.shared.schedule()
|
|
}
|
|
NtfManager.shared.setNtfBadgeCount(chatModel.totalUnreadCountForAllUsers())
|
|
case .active:
|
|
CallController.shared.shouldSuspendChat = false
|
|
let appState = AppChatState.shared.value
|
|
|
|
if appState != .stopped {
|
|
startChatAndActivate {
|
|
if appState.inactive && chatModel.chatRunning == true {
|
|
updateChats()
|
|
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
|
updateCallInvitations()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func setDbContainer() {
|
|
// Uncomment and run once to open DB in app documents folder:
|
|
// dbContainerGroupDefault.set(.documents)
|
|
// v3DBMigrationDefault.set(.offer)
|
|
// to create database in app documents folder also uncomment:
|
|
// let legacyDatabase = true
|
|
let legacyDatabase = hasLegacyDatabase()
|
|
if legacyDatabase, case .documents = dbContainerGroupDefault.get() {
|
|
dbContainerGroupDefault.set(.documents)
|
|
setMigrationState(.offer)
|
|
logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath())*.db")
|
|
} else {
|
|
dbContainerGroupDefault.set(.group)
|
|
setMigrationState(.ready)
|
|
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath())*.db")
|
|
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not") present")
|
|
}
|
|
}
|
|
|
|
private func setMigrationState(_ state: V3DBMigrationState) {
|
|
if case .migrated = v3DBMigrationDefault.get() { return }
|
|
v3DBMigrationDefault.set(state)
|
|
}
|
|
|
|
private func authenticationExpired() -> Bool {
|
|
if let enteredBackgroundAuthenticated = enteredBackgroundAuthenticated {
|
|
let delay = Double(UserDefaults.standard.integer(forKey: DEFAULT_LA_LOCK_DELAY))
|
|
return ProcessInfo.processInfo.systemUptime - enteredBackgroundAuthenticated >= delay
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
|
|
private func updateChats() {
|
|
do {
|
|
let chats = try apiGetChats()
|
|
chatModel.updateChats(with: chats)
|
|
if let id = chatModel.chatId,
|
|
let chat = chatModel.getChat(id) {
|
|
loadChat(chat: chat)
|
|
}
|
|
if let ncr = chatModel.ntfContactRequest {
|
|
chatModel.ntfContactRequest = nil
|
|
if case let .contactRequest(contactRequest) = chatModel.getChat(ncr.chatId)?.chatInfo {
|
|
Task { await acceptContactRequest(incognito: ncr.incognito, contactRequest: contactRequest) }
|
|
}
|
|
}
|
|
} catch let error {
|
|
logger.error("apiGetChats: cannot update chats \(responseError(error))")
|
|
}
|
|
}
|
|
|
|
private func updateCallInvitations() {
|
|
do {
|
|
try refreshCallInvitations()
|
|
} catch let error {
|
|
logger.error("apiGetCallInvitations: cannot update call invitations \(responseError(error))")
|
|
}
|
|
}
|
|
}
|