144 lines
5.5 KiB
Swift
144 lines
5.5 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
// Copyright 2022-2025 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
@main
|
|
struct Application: App {
|
|
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
|
@Environment(\.openURL) private var openURL
|
|
@Environment(\.openWindow) private var openWindow
|
|
@Environment(\.dismissWindow) private var dismissWindow
|
|
|
|
private var appCoordinator: AppCoordinatorProtocol!
|
|
|
|
init() {
|
|
if ProcessInfo.isRunningUITests {
|
|
appCoordinator = UITestsAppCoordinator(appDelegate: appDelegate)
|
|
} else if ProcessInfo.isRunningUnitTests {
|
|
appCoordinator = UnitTestsAppCoordinator(appDelegate: appDelegate)
|
|
} else if ProcessInfo.isRunningAccessibilityTests {
|
|
appCoordinator = AccessibilityTestsAppCoordinator(appDelegate: appDelegate)
|
|
} else {
|
|
appCoordinator = AppCoordinator(appDelegate: appDelegate)
|
|
}
|
|
|
|
SceneDelegate.windowManager = appCoordinator.windowManager
|
|
}
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
appCoordinator.toPresentable()
|
|
.statusBarHidden(shouldHideStatusBar)
|
|
.overlay(alignment: .top) {
|
|
if #available(iOS 26, *), ProcessInfo.processInfo.isiOSAppOnMac {
|
|
// Fake an old-school titlebar to reduce the "floaty-ness" of everything with liquid glass.
|
|
Divider().ignoresSafeArea()
|
|
}
|
|
}
|
|
.environment(\.openURL, openURLAction(appCoordinator: appCoordinator, windowType: nil))
|
|
.onOpenURL { url in
|
|
openURL(url, isExternalURL: true)
|
|
}
|
|
.onContinueUserActivity("INStartVideoCallIntent") { userActivity in
|
|
// `INStartVideoCallIntent` is to be replaced with `INStartCallIntent`
|
|
// but calls from Recents still send it ¯\_(ツ)_/¯
|
|
appCoordinator.handleUserActivity(userActivity)
|
|
}
|
|
.task {
|
|
appCoordinator.start()
|
|
appCoordinator.windowManager.configure(withOpenWinddowAction: openWindow,
|
|
dismissWindowAction: dismissWindow)
|
|
}
|
|
}
|
|
.handlesExternalEvents(matching: ["*"])
|
|
.commands {
|
|
CommandGroup(replacing: .newItem) {
|
|
Button(L10n.actionStartChat) { }
|
|
.disabled(true)
|
|
}
|
|
|
|
CommandGroup(replacing: .appSettings) {
|
|
Button(L10n.commonSettings) {
|
|
appCoordinator.handleAppRoute(.settings, windowType: nil)
|
|
}
|
|
.keyboardShortcut(",", modifiers: .command)
|
|
}
|
|
|
|
CommandGroup(after: .windowArrangement) {
|
|
Button("Global Search") {
|
|
appCoordinator.handleAppRoute(.globalSearch, windowType: nil)
|
|
}
|
|
.keyboardShortcut("k", modifiers: [.command])
|
|
}
|
|
}
|
|
|
|
// This is invoked in response of the WindowManager receiving a register
|
|
// coordinator request and invoking the `OpenWindowAction` with which
|
|
// it's configured in the task above.
|
|
WindowGroup(for: SecondaryWindowType.self) { $type in
|
|
if let type {
|
|
appCoordinator.windowManager.windowForType(type)
|
|
.environment(\.openURL, openURLAction(appCoordinator: appCoordinator, windowType: type))
|
|
}
|
|
}
|
|
.defaultSize(width: ProcessInfo.processInfo.isiOSAppOnMac ? 600 : 400, height: 800)
|
|
.windowResizability(.contentSize)
|
|
}
|
|
|
|
private func openURLAction(appCoordinator: AppCoordinatorProtocol, windowType: SecondaryWindowType?) -> OpenURLAction {
|
|
.init { url in
|
|
if appCoordinator.handleDeepLink(url, isExternalURL: false, windowType: windowType) {
|
|
return .handled
|
|
}
|
|
|
|
if appCoordinator.handlePotentialPhishingAttempt(url: url, openURLAction: { url in
|
|
openURL(url, isExternalURL: false)
|
|
}) {
|
|
return .handled
|
|
}
|
|
|
|
return .systemAction
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
private func openURL(_ url: URL, isExternalURL: Bool) {
|
|
if !appCoordinator.handleDeepLink(url, isExternalURL: isExternalURL, windowType: nil) {
|
|
openURLInSystemBrowser(url)
|
|
}
|
|
}
|
|
|
|
/// Hide the status bar so it doesn't interfere with the screenshot tests
|
|
private var shouldHideStatusBar: Bool {
|
|
ProcessInfo.isRunningUITests
|
|
}
|
|
|
|
/// https://github.com/element-hq/element-x-ios/issues/1824
|
|
/// Avoid opening universal links in other app variants and infinite loops between them
|
|
private func openURLInSystemBrowser(_ originalURL: URL) {
|
|
guard var urlComponents = URLComponents(url: originalURL, resolvingAgainstBaseURL: true) else {
|
|
openURL(originalURL)
|
|
return
|
|
}
|
|
|
|
var queryItems = urlComponents.queryItems ?? []
|
|
queryItems.append(.init(name: "no_universal_links", value: "true"))
|
|
|
|
urlComponents.queryItems = queryItems
|
|
|
|
guard let url = urlComponents.url else {
|
|
openURL(originalURL)
|
|
return
|
|
}
|
|
|
|
openURL(url)
|
|
}
|
|
}
|