diff --git a/ApplicationLibrary/Views/Groups/GroupListView.swift b/ApplicationLibrary/Views/Groups/GroupListView.swift index 09937c8..dcfa222 100644 --- a/ApplicationLibrary/Views/Groups/GroupListView.swift +++ b/ApplicationLibrary/Views/Groups/GroupListView.swift @@ -18,11 +18,7 @@ public struct GroupListView: View { ScrollView { VStack { ForEach(groups, id: \.hashValue) { it in - GroupView(it, Binding(get: { - groupExpand[it.tag] ?? it.selectable - }, set: { newValue in - groupExpand[it.tag] = newValue - })) + GroupView(it) } }.padding() } @@ -44,12 +40,12 @@ public struct GroupListView: View { private func doReload() { if ApplicationLibrary.inPreview { groups = [ - OutboundGroup(tag: "my_group", type: "selector", selected: "server", selectable: true, items: [ + OutboundGroup(tag: "my_group", type: "selector", selected: "server", selectable: true, isExpand: true, items: [ OutboundGroupItem(tag: "server", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: 12), OutboundGroupItem(tag: "server2", type: "WireGuard", urlTestTime: .now, urlTestDelay: 34), OutboundGroupItem(tag: "auto", type: "URLTest", urlTestTime: .now, urlTestDelay: 100), ]), - OutboundGroup(tag: "group2", type: "urltest", selected: "client", selectable: true, items: + OutboundGroup(tag: "group2", type: "urltest", selected: "client", selectable: true, isExpand: true, items: (0 ..< 234).map { index in OutboundGroupItem(tag: "client\(index)", type: "Shadowsocks", urlTestTime: .now, urlTestDelay: UInt16(100 + index * 10)) }), @@ -105,7 +101,7 @@ public struct GroupListView: View { let goItem = itemIterator.next()! items.append(OutboundGroupItem(tag: goItem.tag, type: goItem.type, urlTestTime: Date(timeIntervalSince1970: Double(goItem.urlTestTime)), urlTestDelay: UInt16(goItem.urlTestDelay))) } - groups.append(OutboundGroup(tag: goGroup.tag, type: goGroup.type, selected: goGroup.selected, selectable: goGroup.selectable, items: items)) + groups.append(OutboundGroup(tag: goGroup.tag, type: goGroup.type, selected: goGroup.selected, selectable: goGroup.selectable, isExpand: goGroup.isExpand(), items: items)) } self.groups = groups isLoading = false diff --git a/ApplicationLibrary/Views/Groups/GroupView.swift b/ApplicationLibrary/Views/Groups/GroupView.swift index 9da8e40..ad6e0fc 100644 --- a/ApplicationLibrary/Views/Groups/GroupView.swift +++ b/ApplicationLibrary/Views/Groups/GroupView.swift @@ -3,13 +3,12 @@ import Library import SwiftUI public struct GroupView: View { - @Binding private var expland: Bool @State private var group: OutboundGroup @State private var geometryWidth: CGFloat = 300 + @State private var alert: Alert? - public init(_ group: OutboundGroup, _ expland: Binding) { - self.group = group - _expland = expland + public init(_ group: OutboundGroup) { + _group = State(initialValue: group) } private var title: some View { @@ -25,9 +24,12 @@ public struct GroupView: View { .background(Color.gray.opacity(0.5)) .cornerRadius(4) Button { - expland = !expland + group.isExpand = !group.isExpand + Task.detached { + setGroupExpand() + } } label: { - if expland { + if group.isExpand { Image(systemName: "arrow.down.to.line") } else { Image(systemName: "arrow.up.to.line") @@ -46,12 +48,14 @@ public struct GroupView: View { #if os(macOS) || os(tvOS) .buttonStyle(.plain) #endif - }.padding([.top, .bottom], 8) + } + .alertBinding($alert) + .padding([.top, .bottom], 8) } public var body: some View { Section { - if expland { + if group.isExpand { LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: explandColumnCount())) { @@ -123,7 +127,19 @@ public struct GroupView: View { } private func doURLTest() { - try? LibboxNewStandaloneCommandClient()!.urlTest(group.tag) + do { + try LibboxNewStandaloneCommandClient()!.urlTest(group.tag) + } catch { + alert = Alert(error) + } + } + + private func setGroupExpand() { + do { + try LibboxNewStandaloneCommandClient()!.setGroupExpand(group.tag, isExpand: group.isExpand) + } catch { + alert = Alert(error) + } } } diff --git a/ApplicationLibrary/Views/Groups/OutboundGroup.swift b/ApplicationLibrary/Views/Groups/OutboundGroup.swift index 02e89b0..1e38a39 100644 --- a/ApplicationLibrary/Views/Groups/OutboundGroup.swift +++ b/ApplicationLibrary/Views/Groups/OutboundGroup.swift @@ -7,6 +7,7 @@ public struct OutboundGroup: Codable { let type: String var selected: String let selectable: Bool + var isExpand: Bool let items: [OutboundGroupItem] var hashValue: Int { diff --git a/ApplicationLibrary/Views/Profile/ProfileView.swift b/ApplicationLibrary/Views/Profile/ProfileView.swift index 91072cc..2319771 100644 --- a/ApplicationLibrary/Views/Profile/ProfileView.swift +++ b/ApplicationLibrary/Views/Profile/ProfileView.swift @@ -53,7 +53,7 @@ public struct ProfileView: View { } } FormView { - #if os(iOS) || os(tvOS) + #if os(iOS) NavigationLink { NewProfileView { Task.detached { @@ -64,17 +64,27 @@ public struct ProfileView: View { Text("New Profile").foregroundColor(.accentColor) } .disabled(editMode.isEditing) - #endif - #if os(tvOS) - if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) { + #elseif os(tvOS) + Section { NavigationLink { - ImportProfileView { + NewProfileView { Task.detached { doReload() } } } label: { - Text("Import Profile").foregroundColor(.accentColor) + Text("New Profile").foregroundColor(.accentColor) + } + if ApplicationLibrary.inPreview || devicePickerSupports(.applicationService(name: "sing-box"), parameters: { .applicationService }) { + NavigationLink { + ImportProfileView { + Task.detached { + doReload() + } + } + } label: { + Text("Import Profile").foregroundColor(.accentColor) + } } } #endif diff --git a/ApplicationLibrary/Views/Setting/ServiceLogView.swift b/ApplicationLibrary/Views/Setting/ServiceLogView.swift index 9543686..4593f19 100644 --- a/ApplicationLibrary/Views/Setting/ServiceLogView.swift +++ b/ApplicationLibrary/Views/Setting/ServiceLogView.swift @@ -8,6 +8,7 @@ public struct ServiceLogView: View { public static let windowID = "service-log" #endif + @Environment(\.dismiss) private var dismiss @State private var isLoading = true @State private var content = "" @State private var fileExporterPresented = false @@ -28,7 +29,9 @@ public struct ServiceLogView: View { Text("Empty content") } else { ScrollView { - Text(content).font(logFont) + Text(content) + .font(logFont) + .frame(maxWidth: .infinity, alignment: .topLeading) } .padding() } @@ -36,10 +39,16 @@ public struct ServiceLogView: View { } #if !os(tvOS) .toolbar { - Button("Export") { - fileExporterPresented = true + if !content.isEmpty { + Button("Export") { + fileExporterPresented = true + } + Button("Delete", role: .destructive) { + Task.detached { + deleteContent() + } + } } - .disabled(content.isEmpty) } #endif #if !os(tvOS) @@ -52,11 +61,8 @@ public struct ServiceLogView: View { ) #endif .navigationTitle("Service Log") - #if os(iOS) - .navigationBarTitleDisplayMode(.inline) - #endif #if os(tvOS) - .focusable() + .focusable() #endif } @@ -67,11 +73,21 @@ public struct ServiceLogView: View { if content.isEmpty { do { content = try String(contentsOf: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old")) - } catch {} + } catch { + } } + isLoading = false } + private func deleteContent() { + try? FileManager.default.removeItem(at: FilePath.cacheDirectory.appendingPathComponent("stderr.log")) + try? FileManager.default.removeItem(at: FilePath.cacheDirectory.appendingPathComponent("stderr.log.old")) + DispatchQueue.main.async { + dismiss() + } + } + #if !os(tvOS) private struct LogDocument: FileDocument { static var readableContentTypes = [UTType.text] diff --git a/sing-box.xcodeproj/project.pbxproj b/sing-box.xcodeproj/project.pbxproj index d8ac1e9..7197090 100644 --- a/sing-box.xcodeproj/project.pbxproj +++ b/sing-box.xcodeproj/project.pbxproj @@ -1316,12 +1316,12 @@ 3AEC20F22A459AB400A63465 /* SFI */, 3AEC21082A459B1900A63465 /* SFM */, 3AEECC032A6DF9CA006A0E0C /* SFM.System */, + 3AEECBF02A6DF40A006A0E0C /* SystemExtension */, 3AC03B952A72BF3300B7946F /* SFT */, 3AEC211C2A459B4700A63465 /* Library */, 3A4EAD0F2A4FEAE6005435B3 /* ApplicationLibrary */, 3AEECC2E2A6DFDAD006A0E0C /* MacLibrary */, 3A096F852A4ED3DE00D4A2ED /* Extension */, - 3AEECBF02A6DF40A006A0E0C /* SystemExtension */, 3A77016C2A4E6B34008F031F /* IntentsExtension */, 3AE171982A8128DD00393060 /* TVExtension */, ); @@ -1704,7 +1704,6 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; OTHER_CODE_SIGN_FLAGS = ""; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.extension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1744,7 +1743,6 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; OTHER_CODE_SIGN_FLAGS = ""; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.extension; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1868,7 +1866,6 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; OTHER_CODE_SIGN_FLAGS = ""; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.intents; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1908,7 +1905,6 @@ MACOSX_DEPLOYMENT_TARGET = 13.0; MARKETING_VERSION = 1.0; OTHER_CODE_SIGN_FLAGS = ""; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.intents; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1948,8 +1944,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.7; - OTHER_LDFLAGS = "-ld_classic"; + MARKETING_VERSION = 1.4.0; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa; PRODUCT_NAME = "sing-box"; SDKROOT = appletvos; @@ -1983,8 +1978,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.7; - OTHER_LDFLAGS = "-ld_classic"; + MARKETING_VERSION = 1.4.0; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa; PRODUCT_NAME = "sing-box"; SDKROOT = appletvos; @@ -2015,7 +2009,6 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.extension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2048,7 +2041,6 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.extension; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -2220,9 +2212,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.7; + MARKETING_VERSION = 1.4.0; OTHER_CODE_SIGN_FLAGS = "--deep"; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa; PRODUCT_NAME = "sing-box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2261,9 +2252,8 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.3.7; + MARKETING_VERSION = 1.4.0; OTHER_CODE_SIGN_FLAGS = "--deep"; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa; PRODUCT_NAME = "sing-box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2301,9 +2291,8 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.3.7; + MARKETING_VERSION = 1.4.0; OTHER_CODE_SIGN_FLAGS = ""; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa; PRODUCT_NAME = "sing-box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2340,9 +2329,8 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.3.7; + MARKETING_VERSION = 1.4.0; OTHER_CODE_SIGN_FLAGS = ""; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa; PRODUCT_NAME = "sing-box"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2390,7 +2378,6 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; OTHER_CODE_SIGN_FLAGS = "--deep"; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.library; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2445,7 +2432,6 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; OTHER_CODE_SIGN_FLAGS = "--deep"; - OTHER_LDFLAGS = "-ld_classic"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.library; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2469,6 +2455,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = Z56Z6NYZN2; ENABLE_HARDENED_RUNTIME = YES; @@ -2484,8 +2471,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.3.4; - OTHER_LDFLAGS = "-ld_classic"; + MARKETING_VERSION = "1.4.0-beta.5"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system; PRODUCT_NAME = "$(inherited)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2505,6 +2491,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = Z56Z6NYZN2; ENABLE_HARDENED_RUNTIME = YES; @@ -2520,8 +2507,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 1.3.4; - OTHER_LDFLAGS = "-ld_classic"; + MARKETING_VERSION = "1.4.0-beta.5"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system; PRODUCT_NAME = "$(inherited)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2561,8 +2547,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.4.0-beta.1"; - OTHER_LDFLAGS = "-ld_classic"; + MARKETING_VERSION = "1.4.0-beta.5"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent; PRODUCT_NAME = SFM; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2601,8 +2586,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = "1.4.0-beta.1"; - OTHER_LDFLAGS = "-ld_classic"; + MARKETING_VERSION = "1.4.0-beta.5"; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent; PRODUCT_NAME = SFM; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -2642,7 +2626,7 @@ MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = org.sagernet.sfa.macapp; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.macapp; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; @@ -2682,7 +2666,7 @@ MARKETING_VERSION = 1.0; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++20"; - PRODUCT_BUNDLE_IDENTIFIER = org.sagernet.sfa.macapp; + PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.macapp; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx;