From 45b4e58b3f108f047e5f72e9322ddabcc37497a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Fri, 23 Feb 2024 23:37:31 +0800 Subject: [PATCH] Fix interfaces for tvOS --- .../Views/Abstract/BackButton.swift | 14 +++++++ .../Views/Abstract/FormItem.swift | 15 +++++++ .../Views/Profile/EditProfileView.swift | 4 +- .../Views/Profile/ImportProfileView.swift | 40 ++++++++++++------- .../Views/Profile/ProfileView.swift | 12 +++--- .../Views/Setting/ServiceLogView.swift | 20 ++++++---- .../Views/Setting/SettingView.swift | 33 ++++++++------- sing-box.xcodeproj/project.pbxproj | 4 ++ 8 files changed, 95 insertions(+), 47 deletions(-) create mode 100644 ApplicationLibrary/Views/Abstract/BackButton.swift diff --git a/ApplicationLibrary/Views/Abstract/BackButton.swift b/ApplicationLibrary/Views/Abstract/BackButton.swift new file mode 100644 index 0000000..afcbdeb --- /dev/null +++ b/ApplicationLibrary/Views/Abstract/BackButton.swift @@ -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") + } + } +} diff --git a/ApplicationLibrary/Views/Abstract/FormItem.swift b/ApplicationLibrary/Views/Abstract/FormItem.swift index e6f76a0..b5a3c36 100644 --- a/ApplicationLibrary/Views/Abstract/FormItem.swift +++ b/ApplicationLibrary/Views/Abstract/FormItem.swift @@ -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 +} diff --git a/ApplicationLibrary/Views/Profile/EditProfileView.swift b/ApplicationLibrary/Views/Profile/EditProfileView.swift index 1ec7960..2f4f605 100644 --- a/ApplicationLibrary/Views/Profile/EditProfileView.swift +++ b/ApplicationLibrary/Views/Profile/EditProfileView.swift @@ -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") diff --git a/ApplicationLibrary/Views/Profile/ImportProfileView.swift b/ApplicationLibrary/Views/Profile/ImportProfileView.swift index 6073569..8440f2c 100644 --- a/ApplicationLibrary/Views/Profile/ImportProfileView.swift +++ b/ApplicationLibrary/Views/Profile/ImportProfileView.swift @@ -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 diff --git a/ApplicationLibrary/Views/Profile/ProfileView.swift b/ApplicationLibrary/Views/Profile/ProfileView.swift index 824bc1c..2799a00 100644 --- a/ApplicationLibrary/Views/Profile/ProfileView.swift +++ b/ApplicationLibrary/Views/Profile/ProfileView.swift @@ -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 { diff --git a/ApplicationLibrary/Views/Setting/ServiceLogView.swift b/ApplicationLibrary/Views/Setting/ServiceLogView.swift index e1402ae..7215c07 100644 --- a/ApplicationLibrary/Views/Setting/ServiceLogView.swift +++ b/ApplicationLibrary/Views/Setting/ServiceLogView.swift @@ -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) diff --git a/ApplicationLibrary/Views/Setting/SettingView.swift b/ApplicationLibrary/Views/Setting/SettingView.swift index 9af87c0..6c6c725 100644 --- a/ApplicationLibrary/Views/Setting/SettingView.swift +++ b/ApplicationLibrary/Views/Setting/SettingView.swift @@ -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") } } diff --git a/sing-box.xcodeproj/project.pbxproj b/sing-box.xcodeproj/project.pbxproj index 0219721..1e23c4a 100644 --- a/sing-box.xcodeproj/project.pbxproj +++ b/sing-box.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 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 */; }; 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 */; }; 3A1CF2F22A50E613000A8289 /* OutboundGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F12A50E613000A8289 /* OutboundGroup.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 = ""; }; 3A0C6D3B2A79D46500A4DF2B /* OverviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewView.swift; sourceTree = ""; }; 3A0C6D3D2A79D6A600A4DF2B /* DashboardPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardPage.swift; sourceTree = ""; }; + 3A172D2A2B88E9DB00D98050 /* BackButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackButton.swift; sourceTree = ""; }; 3A1CF2EF2A50E5EE000A8289 /* GroupListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupListView.swift; sourceTree = ""; }; 3A1CF2F12A50E613000A8289 /* OutboundGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutboundGroup.swift; sourceTree = ""; }; 3A1CF2F32A50E937000A8289 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = ""; }; @@ -1018,6 +1020,7 @@ 3ACE6DE22ACADF55009D9A8A /* Binding+Setter.swift */, 3A3AB2A82B70C5F1001815AE /* RequestReviewButton.swift */, 3ACA8B322B7E037800B7238F /* DeleteButton.swift */, + 3A172D2A2B88E9DB00D98050 /* BackButton.swift */, ); path = Abstract; sourceTree = ""; @@ -1477,6 +1480,7 @@ 3AF5E3E72B6F90640058B9E8 /* ProfileOverrideView.swift in Sources */, 3A4EAD302A4FEB77005435B3 /* NewProfileView.swift in Sources */, 3A3AB2A72B70C146001815AE /* CoreView.swift in Sources */, + 3A172D2B2B88E9DB00D98050 /* BackButton.swift in Sources */, 3A4EAD242A4FEB65005435B3 /* InstallProfileButton.swift in Sources */, 3AE4D0C12A6E4852009FEA9E /* InstallSystemExtensionButton.swift in Sources */, 3A4EAD232A4FEB5A005435B3 /* EnvironmentValues.swift in Sources */,