Fix interfaces for tvOS

This commit is contained in:
世界
2024-02-23 23:37:42 +08:00
parent be67111425
commit 45b4e58b3f
8 changed files with 95 additions and 47 deletions
@@ -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) .foregroundColor(.accentColor)
#endif #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") { Section("Action") {
if profile.type != .remote { if profile.type != .remote {
#if os(iOS) || os(macOS) #if os(iOS) || os(macOS)
NavigationLink { FormNavigationLink {
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false)) EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: false))
} label: { } label: {
Label("Edit Content", systemImage: "pencil") Label("Edit Content", systemImage: "pencil")
@@ -66,7 +66,7 @@ public struct EditProfileView: View {
#endif #endif
} else { } else {
#if os(iOS) || os(macOS) #if os(iOS) || os(macOS)
NavigationLink { FormNavigationLink {
EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true)) EditProfileContentView(EditProfileContentView.Context(profileID: profile.id!, readOnly: true))
} label: { } label: {
Label("View Content", systemImage: "doc.fill") Label("View Content", systemImage: "doc.fill")
@@ -22,25 +22,37 @@
} }
public var body: some View { public var body: some View {
VStack { VStack(alignment: .center) {
if !selected { if !selected {
DevicePicker( Form {
.applicationService(name: "sing-box:profile")) Section {
{ endpoint in EmptyView()
selected = true } footer: {
Task { Text("To import configurations from your iPhone or iPad, make sure sing-box is the **same version** on both devices and **VPN is disabled**.")
await handleEndpoint(endpoint) }
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 { } else if let profiles {
Form { Form {
Text("\(profiles.count) Profiles") Section {
EmptyView()
} footer: {
Text("\(profiles.count) Profiles")
}
ForEach(profiles, id: \.profileID) { profile in ForEach(profiles, id: \.profileID) { profile in
Button(profile.name) { Button(profile.name) {
isLoading = true isLoading = true
@@ -45,27 +45,27 @@ public struct ProfileView: View {
} }
FormView { FormView {
#if os(iOS) #if os(iOS)
NavigationLink { FormNavigationLink {
NewProfileView() NewProfileView()
} label: { } label: {
Text("New Profile").foregroundColor(.accentColor) Text("New Profile").foregroundColor(.accentColor)
} }
.disabled(editMode.isEditing) .disabled(editMode.isEditing)
#elseif os(macOS) #elseif os(macOS)
NavigationLink { FormNavigationLink {
NewProfileView() NewProfileView()
} label: { } label: {
Text("New Profile") Text("New Profile")
} }
#elseif os(tvOS) #elseif os(tvOS)
Section { Section {
NavigationLink { FormNavigationLink {
NewProfileView() NewProfileView()
} label: { } label: {
Text("New Profile").foregroundColor(.accentColor) Text("New Profile").foregroundColor(.accentColor)
} }
if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) { if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) {
NavigationLink { FormNavigationLink {
ImportProfileView { ImportProfileView {
await doReload() await doReload()
} }
@@ -291,7 +291,7 @@ public struct ProfileView: View {
private var body0: some View { private var body0: some View {
viewBuilder { viewBuilder {
#if !os(macOS) #if !os(macOS)
NavigationLink { FormNavigationLink {
EditProfileView().environmentObject(profile.origin) EditProfileView().environmentObject(profile.origin)
} label: { } label: {
Text(profile.name) Text(profile.name)
@@ -329,7 +329,7 @@ public struct ProfileView: View {
} }
} }
#else #else
NavigationLink { FormNavigationLink {
EditProfileView().environmentObject(profile.origin) EditProfileView().environmentObject(profile.origin)
} label: { } label: {
HStack { HStack {
@@ -35,24 +35,28 @@ public struct ServiceLogView: View {
} }
} }
} }
#if !os(tvOS)
.toolbar { .toolbar {
if !content.isEmpty { if !content.isEmpty {
ShareButtonCompat($alert) { #if !os(tvOS)
Label("Export", systemImage: "square.and.arrow.up.fill") ShareButtonCompat($alert) {
} itemURL: { Label("Export", systemImage: "square.and.arrow.up.fill")
try content.generateShareFile(name: "service.log") } itemURL: {
} try content.generateShareFile(name: "service.log")
}
#endif
Button(role: .destructive) { Button(role: .destructive) {
Task { Task {
await deleteContent() await deleteContent()
} }
} label: { } label: {
Label("Delete", systemImage: "trash.fill") #if !os(tvOS)
Label("Delete", systemImage: "trash.fill")
#else
Image(systemName: "trash.fill")
#endif
} }
} }
} }
#endif
.alertBinding($alert) .alertBinding($alert)
.navigationTitle("Service Log") .navigationTitle("Service Log")
#if os(tvOS) #if os(tvOS)
@@ -78,7 +78,7 @@ public struct SettingView: View {
@MainActor @MainActor
var navigationLink: some View { var navigationLink: some View {
NavigationLink { FormNavigationLink {
contentView contentView
} label: { } label: {
label label
@@ -98,25 +98,25 @@ public struct SettingView: View {
ForEach([Tabs.core, Tabs.packetTunnel, Tabs.profileOverride]) { it in ForEach([Tabs.core, Tabs.packetTunnel, Tabs.profileOverride]) { it in
it.navigationLink it.navigationLink
} }
Section("About") { #if !os(tvOS)
Link(destination: URL(string: "https://sing-box.sagernet.org/")!) { Section("About") {
Label("Documentation", systemImage: "doc.on.doc.fill") Link(destination: URL(string: "https://sing-box.sagernet.org/")!) {
} Label("Documentation", systemImage: "doc.on.doc.fill")
.buttonStyle(.plain) }
.foregroundColor(.accentColor) .buttonStyle(.plain)
#if !os(tvOS) .foregroundColor(.accentColor)
RequestReviewButton { RequestReviewButton {
Label("Rate on the App Store", systemImage: "text.bubble.fill") Label("Rate on the App Store", systemImage: "text.bubble.fill")
} }
#endif #if os(macOS)
#if os(macOS) if Variant.useSystemExtension {
if Variant.useSystemExtension { Tabs.sponsors.navigationLink
Tabs.sponsors.navigationLink }
} #endif
#endif }
} #endif
Section("Debug") { Section("Debug") {
NavigationLink { FormNavigationLink {
ServiceLogView() ServiceLogView()
} label: { } label: {
Label("Service Log", systemImage: "doc.on.clipboard") Label("Service Log", systemImage: "doc.on.clipboard")
@@ -136,6 +136,5 @@ public struct SettingView: View {
} }
} }
} }
.navigationTitle("Settings")
} }
} }
+4
View File
@@ -11,6 +11,7 @@
3A096F8F2A4ED3DE00D4A2ED /* Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3A096F862A4ED3DE00D4A2ED /* Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 3A096F8F2A4ED3DE00D4A2ED /* Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3A096F862A4ED3DE00D4A2ED /* Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
3A0C6D3C2A79D46500A4DF2B /* OverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */; }; 3A0C6D3C2A79D46500A4DF2B /* OverviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */; };
3A0C6D3E2A79D6A600A4DF2B /* DashboardPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */; }; 3A0C6D3E2A79D6A600A4DF2B /* DashboardPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */; };
3A172D2B2B88E9DB00D98050 /* BackButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A172D2A2B88E9DB00D98050 /* BackButton.swift */; };
3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */; }; 3A1CF2F02A50E5EE000A8289 /* GroupListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */; };
3A1CF2F22A50E613000A8289 /* OutboundGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */; }; 3A1CF2F22A50E613000A8289 /* OutboundGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */; };
3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F52A50EE9C000A8289 /* GroupView.swift */; }; 3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F52A50EE9C000A8289 /* GroupView.swift */; };
@@ -439,6 +440,7 @@
3A096F8C2A4ED3DE00D4A2ED /* Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Extension.entitlements; sourceTree = "<group>"; }; 3A096F8C2A4ED3DE00D4A2ED /* Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Extension.entitlements; sourceTree = "<group>"; };
3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewView.swift; sourceTree = "<group>"; }; 3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewView.swift; sourceTree = "<group>"; };
3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardPage.swift; sourceTree = "<group>"; }; 3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardPage.swift; sourceTree = "<group>"; };
3A172D2A2B88E9DB00D98050 /* BackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackButton.swift; sourceTree = "<group>"; };
3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupListView.swift; sourceTree = "<group>"; }; 3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupListView.swift; sourceTree = "<group>"; };
3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutboundGroup.swift; sourceTree = "<group>"; }; 3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutboundGroup.swift; sourceTree = "<group>"; };
3A1CF2F32A50E937000A8289 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; }; 3A1CF2F32A50E937000A8289 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
@@ -1018,6 +1020,7 @@
3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */, 3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */,
3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */, 3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */,
3ACA8B322B7E037800B7238F /* DeleteButton.swift */, 3ACA8B322B7E037800B7238F /* DeleteButton.swift */,
3A172D2A2B88E9DB00D98050 /* BackButton.swift */,
); );
path = Abstract; path = Abstract;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -1477,6 +1480,7 @@
3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */, 3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */,
3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */, 3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */,
3A3AB2A72B70C146001815AE /* CoreView.swift in Sources */, 3A3AB2A72B70C146001815AE /* CoreView.swift in Sources */,
3A172D2B2B88E9DB00D98050 /* BackButton.swift in Sources */,
3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */, 3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */,
3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */, 3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */,
3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */, 3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */,