105 lines
4.6 KiB
Swift
105 lines
4.6 KiB
Swift
// SPDX-FileCopyrightText: 2026 Kaede Akino
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import SwiftUI
|
|
import TermindApplication
|
|
import TermindDomain
|
|
|
|
public struct NotificationSettingsFields: View {
|
|
private let model: NotificationSettingsModel
|
|
private let service: any NotificationSending
|
|
private let openSettings: () -> Void
|
|
@State private var authorization = NotificationAuthorization.notDetermined
|
|
@Environment(\.scenePhase) private var scenePhase
|
|
|
|
public init(model: NotificationSettingsModel, service: any NotificationSending, openSettings: @escaping () -> Void)
|
|
{
|
|
self.model = model
|
|
self.service = service
|
|
self.openSettings = openSettings
|
|
}
|
|
|
|
public var body: some View {
|
|
Section {
|
|
Toggle(AppStrings.Notifications.enabled, isOn: binding(\.isEnabled))
|
|
if model.preferences.usesSystemNotifications {
|
|
if authorization == .denied {
|
|
Text(AppStrings.CommandNotifications.denied).foregroundStyle(.secondary)
|
|
Button(AppStrings.CommandNotifications.openSettings, action: openSettings)
|
|
} else if authorization == .notDetermined {
|
|
Button(AppStrings.Notifications.allow) {
|
|
Task { authorization = await service.authorization(request: true) }
|
|
}
|
|
}
|
|
}
|
|
if let error = model.storageError { Text(error).foregroundStyle(.red) }
|
|
}
|
|
Section(AppStrings.Navigation.assistant) {
|
|
modePicker(\.assistantMode)
|
|
}
|
|
.disabled(!model.preferences.isEnabled)
|
|
Section {
|
|
modePicker(\.terminalMode)
|
|
Group {
|
|
Picker(AppStrings.Notifications.delivery, selection: binding(\.terminalDelivery)) {
|
|
Text(AppStrings.Notifications.system).tag(
|
|
NotificationPreferences.TerminalDelivery.systemNotifications)
|
|
Text(AppStrings.Notifications.bell).tag(NotificationPreferences.TerminalDelivery.bell)
|
|
}
|
|
.pickerStyle(.menu)
|
|
Stepper(value: binding(\.terminalMinimumSeconds), in: 0...Int.max, step: 1) {
|
|
#if os(macOS)
|
|
HStack {
|
|
Text(AppStrings.CommandNotifications.minimumDuration)
|
|
Spacer()
|
|
Text(model.preferences.terminalMinimumSeconds, format: .number).monospacedDigit()
|
|
}
|
|
#else
|
|
LabeledContent(AppStrings.CommandNotifications.minimumDuration.swiftUIKey) {
|
|
Text(model.preferences.terminalMinimumSeconds, format: .number).monospacedDigit()
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
.disabled(model.preferences.terminalMode == .never)
|
|
} header: {
|
|
Text(AppStrings.Settings.terminal)
|
|
} footer: {
|
|
Text(AppStrings.CommandNotifications.explanation).fixedSize(horizontal: false, vertical: true)
|
|
}
|
|
.disabled(!model.preferences.isEnabled)
|
|
Section {
|
|
modePicker(\.sftpMode)
|
|
} header: {
|
|
Text(AppStrings.SFTP.sftp)
|
|
}
|
|
.disabled(!model.preferences.isEnabled)
|
|
.task { authorization = await service.authorization(request: false) }
|
|
.onChange(of: scenePhase) { _, phase in
|
|
if phase == .active { Task { authorization = await service.authorization(request: false) } }
|
|
}
|
|
}
|
|
|
|
private func modePicker(_ path: WritableKeyPath<NotificationPreferences, NotificationPreferences.Mode>) -> some View
|
|
{
|
|
Picker(AppStrings.CommandNotifications.when, selection: binding(path)) {
|
|
Text(AppStrings.CommandNotifications.never).tag(NotificationPreferences.Mode.never)
|
|
Text(AppStrings.Notifications.unfocused).tag(NotificationPreferences.Mode.unfocused)
|
|
Text(AppStrings.CommandNotifications.always).tag(NotificationPreferences.Mode.always)
|
|
}
|
|
.pickerStyle(.menu)
|
|
}
|
|
|
|
private func binding<Value>(_ path: WritableKeyPath<NotificationPreferences, Value>) -> Binding<Value> {
|
|
Binding(
|
|
get: { model.preferences[keyPath: path] },
|
|
set: { value in
|
|
let previouslyUsedSystem = model.preferences.usesSystemNotifications
|
|
guard model.set(value, for: path) else { return }
|
|
if !previouslyUsedSystem, model.preferences.usesSystemNotifications {
|
|
Task { authorization = await service.authorization(request: true) }
|
|
}
|
|
})
|
|
}
|
|
}
|