Fix interfaces for tvOS
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public struct BackButton: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
public var body: some View {
|
||||
Button {
|
||||
dismiss()
|
||||
} label: {
|
||||
Image(systemName: "chevron.backward")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,3 +83,18 @@ public func FormButton(role: ButtonRole?, action: @escaping () -> Void, @ViewBui
|
||||
.foregroundColor(.accentColor)
|
||||
#endif
|
||||
}
|
||||
|
||||
public func FormNavigationLink(@ViewBuilder destination: () -> some View, @ViewBuilder label: () -> some View) -> some View {
|
||||
#if !os(tvOS)
|
||||
return NavigationLink(destination: destination, label: label)
|
||||
#else
|
||||
return NavigationLink(destination: {
|
||||
destination()
|
||||
.toolbar {
|
||||
ToolbarItemGroup(placement: .topBarLeading) {
|
||||
BackButton()
|
||||
}
|
||||
}
|
||||
}, label: label)
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public struct EditProfileView: View {
|
||||
Section("Action") {
|
||||
if profile.type != .remote {
|
||||
#if os(iOS) || os(macOS)
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
|
||||
} label: {
|
||||
Label("Edit Content", systemImage: "pencil")
|
||||
@@ -66,7 +66,7 @@ public struct EditProfileView: View {
|
||||
#endif
|
||||
} else {
|
||||
#if os(iOS) || os(macOS)
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
|
||||
} label: {
|
||||
Label("View Content", systemImage: "doc.fill")
|
||||
|
||||
@@ -22,25 +22,37 @@
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
VStack {
|
||||
VStack(alignment: .center) {
|
||||
if !selected {
|
||||
DevicePicker(
|
||||
.applicationService(name: "sing-box:profile"))
|
||||
{ endpoint in
|
||||
selected = true
|
||||
Task {
|
||||
await handleEndpoint(endpoint)
|
||||
Form {
|
||||
Section {
|
||||
EmptyView()
|
||||
} footer: {
|
||||
Text("To import configurations from your iPhone or iPad, make sure sing-box is the **same version** on both devices and **VPN is disabled**.")
|
||||
}
|
||||
|
||||
DevicePicker(
|
||||
.applicationService(name: "sing-box:profile"))
|
||||
{ endpoint in
|
||||
selected = true
|
||||
Task {
|
||||
await handleEndpoint(endpoint)
|
||||
}
|
||||
} label: {
|
||||
Text("Select Device")
|
||||
} fallback: {
|
||||
EmptyView()
|
||||
} parameters: {
|
||||
.applicationService
|
||||
}
|
||||
} label: {
|
||||
Text("Select Device")
|
||||
} fallback: {
|
||||
EmptyView()
|
||||
} parameters: {
|
||||
.applicationService
|
||||
}
|
||||
} else if let profiles {
|
||||
Form {
|
||||
Text("\(profiles.count) Profiles")
|
||||
Section {
|
||||
EmptyView()
|
||||
} footer: {
|
||||
Text("\(profiles.count) Profiles")
|
||||
}
|
||||
ForEach(profiles, id: \.profileID) { profile in
|
||||
Button(profile.name) {
|
||||
isLoading = true
|
||||
|
||||
@@ -45,27 +45,27 @@ public struct ProfileView: View {
|
||||
}
|
||||
FormView {
|
||||
#if os(iOS)
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
NewProfileView()
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
}
|
||||
.disabled(editMode.isEditing)
|
||||
#elseif os(macOS)
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
NewProfileView()
|
||||
} label: {
|
||||
Text("New Profile")
|
||||
}
|
||||
#elseif os(tvOS)
|
||||
Section {
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
NewProfileView()
|
||||
} label: {
|
||||
Text("New Profile").foregroundColor(.accentColor)
|
||||
}
|
||||
if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) {
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
ImportProfileView {
|
||||
await doReload()
|
||||
}
|
||||
@@ -291,7 +291,7 @@ public struct ProfileView: View {
|
||||
private var body0: some View {
|
||||
viewBuilder {
|
||||
#if !os(macOS)
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
EditProfileView().environmentObject(profile.origin)
|
||||
} label: {
|
||||
Text(profile.name)
|
||||
@@ -329,7 +329,7 @@ public struct ProfileView: View {
|
||||
}
|
||||
}
|
||||
#else
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
EditProfileView().environmentObject(profile.origin)
|
||||
} label: {
|
||||
HStack {
|
||||
|
||||
@@ -35,24 +35,28 @@ public struct ServiceLogView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
#if !os(tvOS)
|
||||
.toolbar {
|
||||
if !content.isEmpty {
|
||||
ShareButtonCompat($alert) {
|
||||
Label("Export", systemImage: "square.and.arrow.up.fill")
|
||||
} itemURL: {
|
||||
try content.generateShareFile(name: "service.log")
|
||||
}
|
||||
#if !os(tvOS)
|
||||
ShareButtonCompat($alert) {
|
||||
Label("Export", systemImage: "square.and.arrow.up.fill")
|
||||
} itemURL: {
|
||||
try content.generateShareFile(name: "service.log")
|
||||
}
|
||||
#endif
|
||||
Button(role: .destructive) {
|
||||
Task {
|
||||
await deleteContent()
|
||||
}
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash.fill")
|
||||
#if !os(tvOS)
|
||||
Label("Delete", systemImage: "trash.fill")
|
||||
#else
|
||||
Image(systemName: "trash.fill")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.alertBinding($alert)
|
||||
.navigationTitle("Service Log")
|
||||
#if os(tvOS)
|
||||
|
||||
@@ -78,7 +78,7 @@ public struct SettingView: View {
|
||||
|
||||
@MainActor
|
||||
var navigationLink: some View {
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
contentView
|
||||
} label: {
|
||||
label
|
||||
@@ -98,25 +98,25 @@ public struct SettingView: View {
|
||||
ForEach([Tabs.core, Tabs.packetTunnel, Tabs.profileOverride]) { it in
|
||||
it.navigationLink
|
||||
}
|
||||
Section("About") {
|
||||
Link(destination: URL(string: "https://sing-box.sagernet.org/")!) {
|
||||
Label("Documentation", systemImage: "doc.on.doc.fill")
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.foregroundColor(.accentColor)
|
||||
#if !os(tvOS)
|
||||
#if !os(tvOS)
|
||||
Section("About") {
|
||||
Link(destination: URL(string: "https://sing-box.sagernet.org/")!) {
|
||||
Label("Documentation", systemImage: "doc.on.doc.fill")
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.foregroundColor(.accentColor)
|
||||
RequestReviewButton {
|
||||
Label("Rate on the App Store", systemImage: "text.bubble.fill")
|
||||
}
|
||||
#endif
|
||||
#if os(macOS)
|
||||
if Variant.useSystemExtension {
|
||||
Tabs.sponsors.navigationLink
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if os(macOS)
|
||||
if Variant.useSystemExtension {
|
||||
Tabs.sponsors.navigationLink
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
Section("Debug") {
|
||||
NavigationLink {
|
||||
FormNavigationLink {
|
||||
ServiceLogView()
|
||||
} label: {
|
||||
Label("Service Log", systemImage: "doc.on.clipboard")
|
||||
@@ -136,6 +136,5 @@ public struct SettingView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user