Files
sing-box-for-apple/ApplicationLibrary/Views/Setting/MacAppView.swift
T

228 lines
9.2 KiB
Swift

import Library
import SwiftUI
#if os(macOS)
import AppKit
import ServiceManagement
#endif
public struct AppView: View {
@State private var isLoading = true
@State private var startAtLogin = false
@Environment(\.showMenuBarExtra) private var showMenuBarExtra
@State private var menuBarExtraInBackground = false
#if os(macOS)
@State private var rootHelperRegistrationStatus: SMAppService.Status = .notRegistered
#endif
@State private var alert: AlertState?
public init() {}
public var body: some View {
Group {
if isLoading {
ProgressView().onAppear {
Task {
await loadSettings()
}
}
} else {
FormView {
#if os(macOS)
FormToggle("Start At Login", "Launch the application when the system is logged in. If enabled at the same time as `Show in Menu Bar` and `Keep Menu Bar in Background`, the application interface will not be opened automatically.", $startAtLogin) { newValue in
updateLoginItems(newValue)
}
Toggle("Show in Menu Bar", isOn: showMenuBarExtra)
.onChangeCompat(of: showMenuBarExtra.wrappedValue) { newValue in
Task {
await SharedPreferences.showMenuBarExtra.set(newValue)
if !newValue {
menuBarExtraInBackground = false
}
}
}
if showMenuBarExtra.wrappedValue {
Toggle("Keep Menu Bar in Background", isOn: $menuBarExtraInBackground)
.onChangeCompat(of: menuBarExtraInBackground) { newValue in
Task {
await SharedPreferences.menuBarExtraInBackground.set(newValue)
}
}
}
if Variant.useSystemExtension {
Section("System Extension") {
FormButton {
Task {
await updateSystemExtension()
}
} label: {
Label("Update", systemImage: "arrow.down.doc.fill")
}
FormButton(role: .destructive) {
Task {
await uninstallSystemExtension()
}
} label: {
Label("Uninstall", systemImage: "trash.fill").foregroundColor(.red)
}
}
Section {
if rootHelperRegistrationStatus == .enabled {
FormButton {
performHelperAction {
try HelperServiceManager.unregisterRootHelper()
try HelperServiceManager.registerRootHelper()
}
} label: {
Label("Update", systemImage: "arrow.down.doc.fill")
}
FormButton(role: .destructive) {
performHelperAction {
try HelperServiceManager.unregisterRootHelper()
}
} label: {
Label("Uninstall", systemImage: "trash.fill").foregroundColor(.red)
}
} else if rootHelperRegistrationStatus == .requiresApproval {
FormButton {
openHelperSettings()
} label: {
Label("Enable", systemImage: "switch.2")
}
} else {
FormButton {
performHelperAction {
try HelperServiceManager.registerRootHelper()
}
} label: {
Label("Install", systemImage: "square.and.arrow.down.fill")
}
}
} header: {
VStack(alignment: .leading, spacing: 4) {
Text("Helper Service")
Text("This helper service provides process lookup for `process_name` and `process_path` routing rules, and manages the working directory.")
.font(.caption)
.foregroundStyle(.secondary)
.textCase(nil)
}
}
}
#endif
}
}
}
.alert($alert)
.navigationTitle("App")
#if os(iOS)
.navigationBarTitleDisplayMode(.inline)
#endif
}
private func loadSettings() async {
#if os(macOS)
startAtLogin = SMAppService.mainApp.status == .enabled
menuBarExtraInBackground = await SharedPreferences.menuBarExtraInBackground.get()
if Variant.useSystemExtension {
refreshHelperStatus()
}
#endif
isLoading = false
}
#if os(macOS)
private func updateLoginItems(_ startAtLogin: Bool) {
do {
if startAtLogin {
if SMAppService.mainApp.status == .enabled {
try? SMAppService.mainApp.unregister()
}
try SMAppService.mainApp.register()
} else {
try SMAppService.mainApp.unregister()
}
} catch {
alert = AlertState(error: error)
}
}
private func updateSystemExtension() async {
do {
if let result = try await SystemExtension.install(forceUpdate: true) {
switch result {
case .completed:
alert = AlertState(
title: String(localized: "Update"),
message: String(localized: "System Extension updated.")
)
case .willCompleteAfterReboot:
alert = AlertState(
title: String(localized: "Update"),
message: String(localized: "Reboot required.")
)
}
}
} catch {
alert = AlertState(error: error)
}
}
private func uninstallSystemExtension() async {
do {
if let result = try await SystemExtension.uninstall() {
switch result {
case .completed:
alert = AlertState(
title: String(localized: "Uninstall"),
message: String(localized: "System Extension removed.")
)
case .willCompleteAfterReboot:
alert = AlertState(
title: String(localized: "Uninstall"),
message: String(localized: "Reboot required.")
)
}
}
} catch {
alert = AlertState(error: error)
}
}
private func performHelperAction(_ action: () throws -> Void) {
do {
try action()
refreshHelperStatus()
} catch {
alert = AlertState(error: error)
}
}
private func refreshHelperStatus() {
rootHelperRegistrationStatus = HelperServiceManager.rootHelperStatus
}
private func openHelperSettings() {
if #available(macOS 13.0, *) {
SMAppService.openSystemSettingsLoginItems()
return
}
if let url = URL(string: "x-apple.systempreferences:com.apple.preference.users?LoginItems"),
NSWorkspace.shared.open(url)
{
return
}
NSWorkspace.shared.open(URL(fileURLWithPath: "/System/Applications/System Preferences.app"))
}
#endif
}