Add back local profile for tvOS

This commit is contained in:
世界
2026-01-01 19:47:49 +08:00
parent 9b179fe5cd
commit 55e01a89b5
7 changed files with 200 additions and 184 deletions
@@ -6,11 +6,13 @@
/// Using UIButton via UIViewRepresentable bypasses this issue. /// Using UIButton via UIViewRepresentable bypasses this issue.
struct TVToolbarButton: UIViewRepresentable { struct TVToolbarButton: UIViewRepresentable {
let title: String let title: String
var isEnabled: Bool = true
let action: () -> Void let action: () -> Void
func makeUIView(context: Context) -> UIButton { func makeUIView(context: Context) -> UIButton {
let button = UIButton(type: .system) let button = UIButton(type: .system)
button.setTitle(title, for: .normal) button.setTitle(title, for: .normal)
button.isEnabled = isEnabled
button.setContentHuggingPriority(.required, for: .horizontal) button.setContentHuggingPriority(.required, for: .horizontal)
button.setContentCompressionResistancePriority(.required, for: .horizontal) button.setContentCompressionResistancePriority(.required, for: .horizontal)
button.addTarget(context.coordinator, action: #selector(Coordinator.buttonTapped), for: .primaryActionTriggered) button.addTarget(context.coordinator, action: #selector(Coordinator.buttonTapped), for: .primaryActionTriggered)
@@ -19,6 +21,7 @@
func updateUIView(_ uiView: UIButton, context: Context) { func updateUIView(_ uiView: UIButton, context: Context) {
uiView.setTitle(title, for: .normal) uiView.setTitle(title, for: .normal)
uiView.isEnabled = isEnabled
uiView.invalidateIntrinsicContentSize() uiView.invalidateIntrinsicContentSize()
uiView.sizeToFit() uiView.sizeToFit()
context.coordinator.action = action context.coordinator.action = action
@@ -1,4 +1,3 @@
#if os(iOS) || os(macOS)
import Foundation import Foundation
import Library import Library
import SwiftUI import SwiftUI
@@ -76,6 +75,18 @@
} }
} }
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
#elseif os(tvOS)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
if !readOnly {
TVToolbarButton(title: String(localized: "Save"), isEnabled: viewModel.isChanged) {
Task {
await viewModel.saveContent()
}
}
}
}
}
#endif #endif
} }
@@ -104,6 +115,15 @@
@ViewBuilder @ViewBuilder
private var defaultEditorView: some View { private var defaultEditorView: some View {
#if os(tvOS)
ScrollView {
TextField("", text: readOnly ? .constant(viewModel.profileContent) : $viewModel.profileContent, axis: .vertical)
.lineLimit(1000)
.font(Font.system(.caption2, design: .monospaced))
.autocorrectionDisabled(true)
.disabled(readOnly)
}
#else
Group { Group {
if readOnly { if readOnly {
TextEditor(text: .constant(viewModel.profileContent)) TextEditor(text: .constant(viewModel.profileContent))
@@ -117,7 +137,6 @@
.textContentType(.init(rawValue: "")) .textContentType(.init(rawValue: ""))
.padding() .padding()
#endif #endif
}
}
#endif #endif
}
}
@@ -1,4 +1,3 @@
#if os(iOS) || os(macOS)
import Foundation import Foundation
import Library import Library
import SwiftUI import SwiftUI
@@ -70,5 +69,3 @@
try profile.write(profileContent) try profile.write(profileContent)
} }
} }
#endif
@@ -51,8 +51,8 @@ public struct NewProfileView: View {
.multilineTextAlignment(.trailing) .multilineTextAlignment(.trailing)
} }
Picker(selection: $viewModel.profileType) { Picker(selection: $viewModel.profileType) {
#if !os(tvOS)
Text("Local").tag(ProfileType.local) Text("Local").tag(ProfileType.local)
#if !os(tvOS)
Text("iCloud").tag(ProfileType.icloud) Text("iCloud").tag(ProfileType.icloud)
#endif #endif
Text("Remote").tag(ProfileType.remote) Text("Remote").tag(ProfileType.remote)
@@ -190,6 +190,11 @@ public struct NewProfileView: View {
.navigationTitle("New Profile") .navigationTitle("New Profile")
.disabled(viewModel.isSaving) .disabled(viewModel.isSaving)
.alert($viewModel.alert) .alert($viewModel.alert)
.onChangeCompat(of: viewModel.createSucceeded) { newValue in
if newValue {
dismiss()
}
}
#if os(iOS) #if os(iOS)
.fileImporter( .fileImporter(
isPresented: $viewModel.pickerPresented, isPresented: $viewModel.pickerPresented,
@@ -6,6 +6,7 @@ import SwiftUI
@MainActor @MainActor
public final class NewProfileViewModel: BaseViewModel { public final class NewProfileViewModel: BaseViewModel {
@Published public var isSaving = false @Published public var isSaving = false
@Published public var createSucceeded = false
@Published public var profileName = "" @Published public var profileName = ""
#if !os(tvOS) #if !os(tvOS)
@Published public var profileType = ProfileType.local @Published public var profileType = ProfileType.local
@@ -81,6 +82,7 @@ public final class NewProfileViewModel: BaseViewModel {
if sendUpdateNotification { if sendUpdateNotification {
environments.profileUpdate.send() environments.profileUpdate.send()
} }
createSucceeded = true
dismiss?() dismiss?()
#if os(macOS) #if os(macOS)
@@ -18,16 +18,14 @@ public struct ProfileActionToolbar: View {
} }
public var body: some View { public var body: some View {
#if os(iOS) #if os(iOS) || os(tvOS)
iosBody iosBody
#elseif os(tvOS)
tvOSBody
#elseif os(macOS) #elseif os(macOS)
macOSBody macOSBody
#endif #endif
} }
#if os(iOS) #if os(iOS) || os(tvOS)
private var iosBody: some View { private var iosBody: some View {
Section("Action") { Section("Action") {
if profile.type != .remote { if profile.type != .remote {
@@ -49,12 +47,6 @@ public struct ProfileActionToolbar: View {
} }
#endif #endif
#if os(tvOS)
private var tvOSBody: some View {
EmptyView()
}
#endif
#if os(macOS) #if os(macOS)
private var macOSBody: some View { private var macOSBody: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
@@ -1,4 +1,3 @@
#if os(iOS) || os(macOS)
import SwiftUI import SwiftUI
public struct ProfileEditorKey: EnvironmentKey { public struct ProfileEditorKey: EnvironmentKey {
@@ -11,4 +10,3 @@
set { self[ProfileEditorKey.self] = newValue } set { self[ProfileEditorKey.self] = newValue }
} }
} }
#endif