Fix macOS toggle style
This commit is contained in:
@@ -51,13 +51,35 @@ public func FormItem(_ title: String, @ViewBuilder content: () -> some View) ->
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public func FormSection(@ViewBuilder content: () -> some View, @ViewBuilder footer: () -> some View) -> some View {
|
public func FormToggle(_ titleKey: LocalizedStringKey, _ subtitleKey: LocalizedStringKey, _ isOn: Binding<Bool>, _ action: @escaping (_ newValue: Bool) async -> Void) -> some View {
|
||||||
Section {
|
#if os(macOS)
|
||||||
content()
|
Toggle(isOn: isOn) {
|
||||||
} footer: {
|
VStack(alignment: .leading) {
|
||||||
footer()
|
Text(titleKey)
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
Spacer()
|
||||||
}
|
Text(subtitleKey)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChangeCompat(of: isOn.wrappedValue) { newValue in
|
||||||
|
Task {
|
||||||
|
await action(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
Section {
|
||||||
|
Toggle(titleKey, isOn: isOn)
|
||||||
|
.onChangeCompat(of: isOn.wrappedValue) { newValue in
|
||||||
|
Task {
|
||||||
|
await action(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text(subtitleKey)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public func FormButton(action: @escaping () -> Void, @ViewBuilder label: () -> some View) -> some View {
|
public func FormButton(action: @escaping () -> Void, @ViewBuilder label: () -> some View) -> some View {
|
||||||
|
|||||||
@@ -75,10 +75,9 @@
|
|||||||
private func reset() {
|
private func reset() {
|
||||||
selected = false
|
selected = false
|
||||||
profiles = nil
|
profiles = nil
|
||||||
if let connection = connection {
|
if let connection {
|
||||||
connection.cancel()
|
connection.cancel()
|
||||||
self.connection = nil
|
self.connection = nil
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +86,7 @@
|
|||||||
self.connection = NWSocket(connection)
|
self.connection = NWSocket(connection)
|
||||||
connection.stateUpdateHandler = { state in
|
connection.stateUpdateHandler = { state in
|
||||||
switch state {
|
switch state {
|
||||||
case .failed(let error):
|
case let .failed(error):
|
||||||
DispatchQueue.main.async { [self] in
|
DispatchQueue.main.async { [self] in
|
||||||
reset()
|
reset()
|
||||||
alert = Alert(error)
|
alert = Alert(error)
|
||||||
|
|||||||
@@ -25,15 +25,8 @@
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FormView {
|
FormView {
|
||||||
FormSection {
|
FormToggle("Start At Login", "Launch the application when the system is logged in. If enabled at the same time as `Show in Menu Bar` and `Keep Menu Bar in Background`, the application interface will not be opened automatically.", $startAtLogin) { newValue in
|
||||||
Toggle("Start At Login", isOn: $startAtLogin)
|
updateLoginItems(newValue)
|
||||||
.onChangeCompat(of: startAtLogin) { newValue in
|
|
||||||
Task {
|
|
||||||
updateLoginItems(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("Launch the application when the system is logged in. If enabled at the same time as `Show in Menu Bar` and `Keep Menu Bar in Background`, the application interface will not be opened automatically.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Toggle("Show in Menu Bar", isOn: showMenuBarExtra)
|
Toggle("Show in Menu Bar", isOn: showMenuBarExtra)
|
||||||
|
|||||||
@@ -17,19 +17,12 @@ public struct OnDemandRulesView: View {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FormView {
|
FormView {
|
||||||
FormSection {
|
FormToggle("Always On", """
|
||||||
Toggle("Always On", isOn: $alwaysOn)
|
Implement always-on via on-demand rules.
|
||||||
.onChangeCompat(of: alwaysOn) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.alwaysOn.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("""
|
|
||||||
Implement always-on via on-demand rules.
|
|
||||||
|
|
||||||
This should not be an intended use of the API, so you cannot disable VPN in system settings. To stop the service manually, use the in-app interface or simply delete the VPN profile.
|
This should not be an intended use of the API, so you cannot disable VPN in system settings. To stop the service manually, use the in-app interface or simply delete the VPN profile.
|
||||||
""")
|
""", $alwaysOn) { newValue in
|
||||||
|
await SharedPreferences.alwaysOn.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
FormButton {
|
FormButton {
|
||||||
|
|||||||
@@ -23,97 +23,55 @@ struct PacketTunnelView: View {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FormView {
|
FormView {
|
||||||
FormSection {
|
FormToggle("Ignore Memory Limit", """
|
||||||
Toggle("Ignore Memory Limit", isOn: $ignoreMemoryLimit)
|
Do not enforce memory limits on sing-box. Will cause OOM on non-jailbroken iOS and tvOS devices.
|
||||||
.onChangeCompat(of: ignoreMemoryLimit) { newValue in
|
""", $ignoreMemoryLimit) { newValue in
|
||||||
Task {
|
await SharedPreferences.ignoreMemoryLimit.set(newValue)
|
||||||
await SharedPreferences.ignoreMemoryLimit.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("Do not enforce memory limits on sing-box. Will cause OOM on non-jailbroken iOS and tvOS devices.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
|
FormToggle("includeAllNetworks", """
|
||||||
|
If this property is true, the system routes network traffic through the tunnel except traffic for designated system services necessary for maintaining expected device functionality. You can exclude some types of traffic using the **excludeAPNs**, **excludeLocalNetworks**, and **excludeCellularServices** properties in combination with this property.
|
||||||
|
|
||||||
FormSection {
|
when enabled, the default TUN stack is changed to `gvisor`, and the `system` and `mixed` stacks are not available.
|
||||||
Toggle("includeAllNetworks", isOn: $includeAllNetworks)
|
|
||||||
.onChangeCompat(of: includeAllNetworks) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.includeAllNetworks.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("""
|
|
||||||
If this property is true, the system routes network traffic through the tunnel except traffic for designated system services necessary for maintaining expected device functionality. You can exclude some types of traffic using the **excludeAPNs**, **excludeLocalNetworks**, and **excludeCellularServices** properties in combination with this property.
|
|
||||||
|
|
||||||
when enabled, the default TUN stack is changed to `gvisor`, and the `system` and `mixed` stacks are not available.
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3131931-includeallnetworks)
|
||||||
|
""", $includeAllNetworks) { newValue in
|
||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3131931-includeallnetworks)
|
await SharedPreferences.includeAllNetworks.set(newValue)
|
||||||
""")
|
|
||||||
.multilineTextAlignment(.leading)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormSection {
|
FormToggle("excludeAPNs", """
|
||||||
Toggle("excludeAPNs", isOn: $excludeAPNs)
|
If this property is true, the system excludes Apple Push Notification services (APNs) traffic, but only when the **includeAllNetworks** property is also true.
|
||||||
.onChangeCompat(of: excludeAPNs) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.excludeAPNs.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("""
|
|
||||||
If this property is true, the system excludes Apple Push Notification services (APNs) traffic, but only when the **includeAllNetworks** property is also true.
|
|
||||||
|
|
||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140516-excludeapns)
|
||||||
""")
|
""", $excludeAPNs) { newValue in
|
||||||
|
await SharedPreferences.excludeAPNs.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
FormSection {
|
FormToggle("excludeCellularServices", """
|
||||||
Toggle("excludeCellularServices", isOn: $excludeCellularServices)
|
If this property is true, the system excludes cellular services — such as Wi-Fi Calling, MMS, SMS, and Visual Voicemail — but only when the **includeAllNetworks** property is also true. This property doesn’t impact services that use the cellular network only — such as VoLTE — which the system automatically excludes.
|
||||||
.onChangeCompat(of: excludeCellularServices) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.excludeCellularServices.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("""
|
|
||||||
If this property is true, the system excludes cellular services — such as Wi-Fi Calling, MMS, SMS, and Visual Voicemail — but only when the **includeAllNetworks** property is also true. This property doesn’t impact services that use the cellular network only — such as VoLTE — which the system automatically excludes.
|
|
||||||
|
|
||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140517-excludecellularservices)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/4140517-excludecellularservices)
|
||||||
""")
|
""", $excludeCellularServices) { newValue in
|
||||||
|
await SharedPreferences.excludeCellularServices.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
FormSection {
|
FormToggle("excludeLocalNetworks", """
|
||||||
Toggle("excludeLocalNetworks", isOn: $excludeLocalNetworks)
|
If this property is true, the system excludes network connections to hosts on the local network — such as AirPlay, AirDrop, and CarPlay — but only when the **includeAllNetworks** or **enforceRoutes** property is also true.
|
||||||
.onChangeCompat(of: excludeLocalNetworks) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.excludeLocalNetworks.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("""
|
|
||||||
If this property is true, the system excludes network connections to hosts on the local network — such as AirPlay, AirDrop, and CarPlay — but only when the **includeAllNetworks** or **enforceRoutes** property is also true.
|
|
||||||
|
|
||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3143658-excludelocalnetworks)
|
||||||
""")
|
""", $excludeLocalNetworks) { newValue in
|
||||||
|
await SharedPreferences.excludeLocalNetworks.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
FormSection {
|
FormToggle("enforceRoutes", """
|
||||||
Toggle("enforceRoutes", isOn: $enforceRoutes)
|
If this property is true when the **includeAllNetworks** property is false, the system scopes the included routes to the VPN and the excluded routes to the current primary network interface. This property supersedes the system routing table and scoping operations by apps.
|
||||||
.onChangeCompat(of: enforceRoutes) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.enforceRoutes.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("""
|
|
||||||
If this property is true when the **includeAllNetworks** property is false, the system scopes the included routes to the VPN and the excluded routes to the current primary network interface. This property supersedes the system routing table and scoping operations by apps.
|
|
||||||
|
|
||||||
If you set both the **enforceRoutes** and **excludeLocalNetworks** properties to true, the system excludes network connections to hosts on the local network.
|
If you set both the **enforceRoutes** and **excludeLocalNetworks** properties to true, the system excludes network connections to hosts on the local network.
|
||||||
|
|
||||||
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3689459-enforceroutes)
|
[Apple Documentation](https://developer.apple.com/documentation/networkextension/nevpnprotocol/3689459-enforceroutes)
|
||||||
""")
|
""", $enforceRoutes) { newValue in
|
||||||
|
await SharedPreferences.enforceRoutes.set(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,37 +18,18 @@ public struct ProfileOverrideView: View {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FormView {
|
FormView {
|
||||||
FormSection {
|
FormToggle("Hide VPN Icon", "Append `0.0.0.0/31` to `inet4_route_exclude_address` if not exists.", $excludeDefaultRoute) { newValue in
|
||||||
Toggle("Hide VPN Icon", isOn: $excludeDefaultRoute)
|
await SharedPreferences.excludeDefaultRoute.set(newValue)
|
||||||
.onChangeCompat(of: excludeDefaultRoute) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.excludeDefaultRoute.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("Append `0.0.0.0/31` to `inet4_route_exclude_address` if not exists.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormSection {
|
FormToggle("No Default Route", """
|
||||||
Toggle("No Default Route", isOn: $autoRouteUseSubRangesByDefault)
|
By default, segment routing is used in `auto_route` instead of global routing. If `*_<route_address/route_exclude_address>` exists in the configuration, this item will not take effect on the corresponding network (commonly used to resolve HomeKit compatibility issues).
|
||||||
.onChangeCompat(of: autoRouteUseSubRangesByDefault) { newValue in
|
""", $autoRouteUseSubRangesByDefault) { newValue in
|
||||||
Task {
|
await SharedPreferences.autoRouteUseSubRangesByDefault.set(newValue)
|
||||||
await SharedPreferences.autoRouteUseSubRangesByDefault.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("By default, segment routing is used in `auto_route` instead of global routing. If `*_<route_address/route_exclude_address>` exists in the configuration, this item will not take effect on the corresponding network. (commonly used to resolve HomeKit compatibility issues)")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormSection {
|
FormToggle("Exclude APNs Route", "Append `push.apple.com` to `bypass_domain`, and `17.0.0.0/8` to `inet4_route_exclude_address`.", $excludeAPNsRoute) { newValue in
|
||||||
Toggle("Exclude APNs Route", isOn: $excludeAPNsRoute)
|
await SharedPreferences.excludeAPNsRoute.set(newValue)
|
||||||
.onChangeCompat(of: excludeAPNsRoute) { newValue in
|
|
||||||
Task {
|
|
||||||
await SharedPreferences.excludeAPNsRoute.set(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} footer: {
|
|
||||||
Text("Append `push.apple.com` to `bypass_domain`, and `17.0.0.0/8` to `inet4_route_exclude_address`.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormButton {
|
FormButton {
|
||||||
|
|||||||
Reference in New Issue
Block a user