Use block versions of Section header/footer
This commit is contained in:
parent
d8b19b952a
commit
48d499569b
|
@ -69,22 +69,20 @@ struct AboutView: View {
|
|||
}
|
||||
|
||||
private var githubSubview: some View {
|
||||
Section(
|
||||
header: Text(Unlocalized.About.github)
|
||||
) {
|
||||
Section {
|
||||
Button(Unlocalized.About.readme) {
|
||||
URL.openURL(readmeURL)
|
||||
}
|
||||
Button(Unlocalized.About.changelog) {
|
||||
URL.openURL(changelogURL)
|
||||
}
|
||||
} header: {
|
||||
Text(Unlocalized.About.github)
|
||||
}
|
||||
}
|
||||
|
||||
private var webSubview: some View {
|
||||
Section(
|
||||
header: Text(L10n.About.Sections.Web.header)
|
||||
) {
|
||||
Section {
|
||||
Button(L10n.About.Items.Website.caption) {
|
||||
URL.openURL(readmeURL)
|
||||
}
|
||||
|
@ -97,6 +95,8 @@ struct AboutView: View {
|
|||
Button(L10n.About.Items.PrivacyPolicy.caption) {
|
||||
URL.openURL(privacyURL)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.About.Sections.Web.header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,11 +63,7 @@ struct AccountView: View {
|
|||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(
|
||||
footer: metadata?.localizedGuidanceString.map {
|
||||
Text($0)
|
||||
}
|
||||
) {
|
||||
Section {
|
||||
TextField(usernamePlaceholder ?? L10n.Account.Items.Username.placeholder, text: $liveAccount.username)
|
||||
.textContentType(.username)
|
||||
.disableAutocorrection(true)
|
||||
|
@ -85,6 +81,10 @@ struct AccountView: View {
|
|||
.disableAutocorrection(true)
|
||||
.autocapitalization(.none)
|
||||
.withLeadingText(L10n.Account.Items.Password.caption)
|
||||
} footer: {
|
||||
metadata?.localizedGuidanceString.map {
|
||||
Text($0)
|
||||
}
|
||||
}
|
||||
if vpnProtocol == .openVPN {
|
||||
metadata?.openVPNGuidanceURL.map { guidanceURL in
|
||||
|
|
|
@ -109,26 +109,27 @@ extension AddHostView {
|
|||
}
|
||||
|
||||
private var encryptionSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.encryption)
|
||||
) {
|
||||
Section {
|
||||
SecureField(L10n.AddProfile.Host.Sections.Encryption.footer, text: $viewModel.encryptionPassphrase) {
|
||||
processProfile(replacingExisting: false)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.encryption)
|
||||
}
|
||||
}
|
||||
|
||||
private var completeSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.AddProfile.Shared.title),
|
||||
footer: themeErrorMessage(viewModel.errorMessage)
|
||||
) {
|
||||
Section {
|
||||
Text(Unlocalized.Network.url)
|
||||
.withTrailingText(url.lastPathComponent)
|
||||
viewModel.processedProfile.vpnProtocols.first.map {
|
||||
Text(L10n.Global.Strings.protocol)
|
||||
.withTrailingText($0.description)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.AddProfile.Shared.title)
|
||||
} footer: {
|
||||
themeErrorMessage(viewModel.errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,12 +39,13 @@ enum AddProfileView {
|
|||
let onCommit: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.name),
|
||||
footer: themeErrorMessage(errorMessage)
|
||||
) {
|
||||
Section {
|
||||
TextField(L10n.Global.Placeholders.profileName, text: $profileName, onCommit: onCommit)
|
||||
.themeValidProfileName()
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.name)
|
||||
} footer: {
|
||||
themeErrorMessage(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,10 +56,10 @@ enum AddProfileView {
|
|||
@Binding var profileName: String
|
||||
|
||||
var body: some View {
|
||||
Section(
|
||||
header: Text(L10n.AddProfile.Shared.Views.Existing.header)
|
||||
) {
|
||||
Section {
|
||||
ForEach(headers, content: existingProfileButton)
|
||||
} header: {
|
||||
Text(L10n.AddProfile.Shared.Views.Existing.header)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,9 +84,7 @@ struct AddProviderView: View {
|
|||
}
|
||||
|
||||
private var mainSection: some View {
|
||||
Section(
|
||||
footer: Text(L10n.AddProfile.Provider.Sections.Vpn.footer)
|
||||
) {
|
||||
Section {
|
||||
let protos = availableVPNProtocols
|
||||
if !protos.isEmpty {
|
||||
themeTextPicker(
|
||||
|
@ -97,14 +95,16 @@ struct AddProviderView: View {
|
|||
)
|
||||
}
|
||||
updateListButton
|
||||
} footer: {
|
||||
Text(L10n.AddProfile.Provider.Sections.Vpn.footer)
|
||||
}
|
||||
}
|
||||
|
||||
private var providersSection: some View {
|
||||
Section(
|
||||
footer: themeErrorMessage(viewModel.errorMessage)
|
||||
) {
|
||||
Section {
|
||||
ForEach(providers, content: providerRow)
|
||||
} footer: {
|
||||
themeErrorMessage(viewModel.errorMessage)
|
||||
}.disabled(viewModel.isFetchingAnyProvider)
|
||||
}
|
||||
|
||||
|
|
|
@ -110,9 +110,7 @@ extension DiagnosticsView {
|
|||
}
|
||||
|
||||
private var debugLogSection: some View {
|
||||
Section(
|
||||
footer: Text(L10n.Diagnostics.Sections.DebugLog.footer)
|
||||
) {
|
||||
Section {
|
||||
let url = debugLogURL
|
||||
NavigationLink(L10n.DebugLog.title) {
|
||||
url.map {
|
||||
|
@ -123,6 +121,8 @@ extension DiagnosticsView {
|
|||
}
|
||||
}.disabled(url == nil)
|
||||
Toggle(L10n.Diagnostics.Items.MasksPrivateData.caption, isOn: $appManager.masksPrivateData)
|
||||
} footer: {
|
||||
Text(L10n.Diagnostics.Sections.DebugLog.footer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -95,16 +95,17 @@ struct DonateView: View {
|
|||
}
|
||||
|
||||
private var productsSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Donate.Sections.OneTime.header),
|
||||
footer: Text(L10n.Donate.Sections.OneTime.footer)
|
||||
.xxxThemeTruncation()
|
||||
) {
|
||||
Section {
|
||||
if !productManager.isRefreshingProducts {
|
||||
ForEach(productManager.donations, id: \.productIdentifier, content: productRow)
|
||||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Donate.Sections.OneTime.header)
|
||||
} footer: {
|
||||
Text(L10n.Donate.Sections.OneTime.footer)
|
||||
.xxxThemeTruncation()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,9 +63,7 @@ extension EndpointAdvancedView {
|
|||
extension EndpointAdvancedView.OpenVPNView {
|
||||
private var ipv4Section: some View {
|
||||
builder.ipv4.map { cfg in
|
||||
Section(
|
||||
header: Text(Unlocalized.Network.ipv4)
|
||||
) {
|
||||
Section {
|
||||
themeLongContentLinkDefault(
|
||||
L10n.Global.Strings.address,
|
||||
content: .constant(builder.ipv4.localizedAddress)
|
||||
|
@ -81,15 +79,15 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
content: .constant(route.localizedDescription)
|
||||
)
|
||||
}
|
||||
} header: {
|
||||
Text(Unlocalized.Network.ipv4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var ipv6Section: some View {
|
||||
builder.ipv6.map { cfg in
|
||||
Section(
|
||||
header: Text(Unlocalized.Network.ipv6)
|
||||
) {
|
||||
Section {
|
||||
themeLongContentLinkDefault(
|
||||
L10n.Global.Strings.address,
|
||||
content: .constant(builder.ipv6.localizedAddress)
|
||||
|
@ -105,15 +103,15 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
content: .constant(route.localizedDescription)
|
||||
)
|
||||
}
|
||||
} header: {
|
||||
Text(Unlocalized.Network.ipv6)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func communicationSection(configuration: OpenVPN.Configuration) -> some View {
|
||||
configuration.communicationSettings.map { settings in
|
||||
Section(
|
||||
header: Text(L10n.Endpoint.Advanced.Openvpn.Sections.Communication.header)
|
||||
) {
|
||||
Section {
|
||||
settings.cipher.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.Cipher.caption)
|
||||
.withTrailingText($0.localizedDescription)
|
||||
|
@ -126,14 +124,14 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
Text(Unlocalized.VPN.xor)
|
||||
.withTrailingText($0.localizedDescriptionAsXOR)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Sections.Communication.header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var communicationEditableSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Endpoint.Advanced.Openvpn.Sections.Communication.header)
|
||||
) {
|
||||
Section {
|
||||
themeTextPicker(
|
||||
L10n.Endpoint.Advanced.Openvpn.Items.Cipher.caption,
|
||||
selection: $builder.cipher ?? OpenVPN.Configuration.Fallback.cipher,
|
||||
|
@ -150,14 +148,14 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
Text(Unlocalized.VPN.xor)
|
||||
.withTrailingText($0.localizedDescriptionAsXOR)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Sections.Communication.header)
|
||||
}
|
||||
}
|
||||
|
||||
private func compressionSection(configuration: OpenVPN.Configuration) -> some View {
|
||||
configuration.compressionSettings.map { settings in
|
||||
Section(
|
||||
header: Text(L10n.Endpoint.Advanced.Openvpn.Sections.Compression.header)
|
||||
) {
|
||||
Section {
|
||||
settings.framing.map {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.CompressionFraming.caption)
|
||||
.withTrailingText($0.localizedDescription)
|
||||
|
@ -166,14 +164,14 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
Text(L10n.Endpoint.Advanced.Openvpn.Items.CompressionAlgorithm.caption)
|
||||
.withTrailingText($0.localizedDescription)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Sections.Compression.header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var compressionEditableSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Endpoint.Advanced.Openvpn.Sections.Compression.header)
|
||||
) {
|
||||
Section {
|
||||
themeTextPicker(
|
||||
L10n.Endpoint.Advanced.Openvpn.Items.CompressionFraming.caption,
|
||||
selection: $builder.compressionFraming ?? OpenVPN.Configuration.Fallback.compressionFraming,
|
||||
|
@ -186,14 +184,14 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
values: OpenVPN.CompressionAlgorithm.available,
|
||||
description: \.localizedDescription
|
||||
).disabled(builder.compressionFraming == .disabled)
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Sections.Compression.header)
|
||||
}
|
||||
}
|
||||
|
||||
private func dnsSection(configuration: OpenVPN.Configuration) -> some View {
|
||||
configuration.dnsSettings.map { settings in
|
||||
Section(
|
||||
header: Text(Unlocalized.Network.dns)
|
||||
) {
|
||||
Section {
|
||||
ForEach(settings.servers, id: \.self) {
|
||||
Text(L10n.Global.Strings.address)
|
||||
.withTrailingText($0, copyOnTap: true)
|
||||
|
@ -202,15 +200,15 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
Text(L10n.Global.Strings.domain)
|
||||
.withTrailingText($0, copyOnTap: true)
|
||||
}
|
||||
} header: {
|
||||
Text(Unlocalized.Network.dns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func proxySection(configuration: OpenVPN.Configuration) -> some View {
|
||||
configuration.proxySettings.map { settings in
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.proxy)
|
||||
) {
|
||||
Section {
|
||||
settings.proxy.map {
|
||||
Text(L10n.Global.Strings.address)
|
||||
.withTrailingText($0.rawValue, copyOnTap: true)
|
||||
|
@ -223,14 +221,14 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
Text(L10n.NetworkSettings.Items.ProxyBypass.caption)
|
||||
.withTrailingText($0, copyOnTap: true)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.proxy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var tlsSection: some View {
|
||||
Section(
|
||||
header: Text(Unlocalized.Network.tls)
|
||||
) {
|
||||
Section {
|
||||
builder.ca.map { ca in
|
||||
themeLongContentLink(
|
||||
Unlocalized.VPN.certificateAuthority,
|
||||
|
@ -258,14 +256,14 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
}
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Items.Eku.caption)
|
||||
.withTrailingText(builder.checksEKU.localizedDescriptionAsEKU)
|
||||
} header: {
|
||||
Text(Unlocalized.Network.tls)
|
||||
}
|
||||
}
|
||||
|
||||
private func otherSection(configuration: OpenVPN.Configuration) -> some View {
|
||||
configuration.otherSettings.map { settings in
|
||||
Section(
|
||||
header: Text(L10n.Endpoint.Advanced.Openvpn.Sections.Other.header)
|
||||
) {
|
||||
Section {
|
||||
settings.keepAlive.map {
|
||||
Text(L10n.Global.Strings.keepalive)
|
||||
.withTrailingText($0.localizedDescriptionAsKeepAlive)
|
||||
|
@ -278,6 +276,8 @@ extension EndpointAdvancedView.OpenVPNView {
|
|||
Text(L10n.Endpoint.Advanced.Openvpn.Items.RandomEndpoint.caption)
|
||||
.withTrailingText($0.localizedDescriptionAsRandomizeEndpoint)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Advanced.Openvpn.Sections.Other.header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,27 +46,25 @@ extension EndpointAdvancedView {
|
|||
|
||||
extension EndpointAdvancedView.WireGuardView {
|
||||
private var keySection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.interface)
|
||||
) {
|
||||
Section {
|
||||
themeLongContentLink(L10n.Global.Strings.privateKey, content: .constant(builder.privateKey))
|
||||
themeLongContentLink(L10n.Global.Strings.publicKey, content: .constant(builder.publicKey))
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.interface)
|
||||
}
|
||||
}
|
||||
|
||||
private var addressesSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.addresses)
|
||||
) {
|
||||
Section {
|
||||
ForEach(builder.addresses, id: \.self, content: Text.init)
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.addresses)
|
||||
}
|
||||
}
|
||||
|
||||
private func dnsSection(configuration: WireGuard.Configuration) -> some View {
|
||||
configuration.dnsSettings.map { settings in
|
||||
Section(
|
||||
header: Text(Unlocalized.Network.dns)
|
||||
) {
|
||||
Section {
|
||||
ForEach(settings.servers, id: \.self) {
|
||||
Text(L10n.Global.Strings.address)
|
||||
.withTrailingText($0)
|
||||
|
@ -75,6 +73,8 @@ extension EndpointAdvancedView.WireGuardView {
|
|||
Text(L10n.Global.Strings.domain)
|
||||
.withTrailingText($0)
|
||||
}
|
||||
} header: {
|
||||
Text(Unlocalized.Network.dns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,12 +154,12 @@ extension EndpointView.OpenVPNView {
|
|||
}
|
||||
|
||||
private var addressesSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.addresses)
|
||||
) {
|
||||
Section {
|
||||
filteredRemotes.map {
|
||||
ForEach($0, content: button(forEndpoint:))
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.addresses)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,9 +104,7 @@ extension EndpointView.WireGuardView {
|
|||
}
|
||||
|
||||
private func section(forPeerAt peerIndex: Int) -> some View {
|
||||
Section(
|
||||
header: Text(L10n.Endpoint.Wireguard.Items.Peer.caption)
|
||||
) {
|
||||
Section {
|
||||
themeLongContentLink(
|
||||
L10n.Global.Strings.publicKey,
|
||||
content: .constant(builder.publicKey(ofPeer: peerIndex))
|
||||
|
@ -129,6 +127,8 @@ extension EndpointView.WireGuardView {
|
|||
Text(L10n.Global.Strings.keepalive)
|
||||
.withTrailingText(TimeInterval($0).localizedDescriptionAsKeepAlive)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Endpoint.Wireguard.Items.Peer.caption)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,15 +97,15 @@ struct NetworkSettingsView: View {
|
|||
|
||||
extension NetworkSettingsView {
|
||||
private var gatewayView: some View {
|
||||
Section(
|
||||
header: Text(L10n.NetworkSettings.Gateway.title)
|
||||
) {
|
||||
Section {
|
||||
Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticGateway.themeAnimation())
|
||||
|
||||
if !settings.isAutomaticGateway {
|
||||
Toggle(Unlocalized.Network.ipv4, isOn: $settings.gateway.isDefaultIPv4)
|
||||
Toggle(Unlocalized.Network.ipv6, isOn: $settings.gateway.isDefaultIPv6)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.NetworkSettings.Gateway.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,9 +116,7 @@ extension NetworkSettingsView {
|
|||
|
||||
@ViewBuilder
|
||||
private var dnsView: some View {
|
||||
Section(
|
||||
header: Text(Unlocalized.Network.dns)
|
||||
) {
|
||||
Section {
|
||||
Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticDNS.themeAnimation())
|
||||
|
||||
if !settings.isAutomaticDNS {
|
||||
|
@ -143,6 +141,8 @@ extension NetworkSettingsView {
|
|||
EmptyView()
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(Unlocalized.Network.dns)
|
||||
}
|
||||
if !settings.isAutomaticDNS && settings.dns.configurationType != .disabled {
|
||||
dnsManualServers
|
||||
|
@ -209,9 +209,7 @@ extension NetworkSettingsView {
|
|||
|
||||
@ViewBuilder
|
||||
private var proxyView: some View {
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.proxy)
|
||||
) {
|
||||
Section {
|
||||
Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticProxy.themeAnimation())
|
||||
|
||||
if !settings.isAutomaticProxy {
|
||||
|
@ -240,6 +238,8 @@ extension NetworkSettingsView {
|
|||
EmptyView()
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.proxy)
|
||||
}
|
||||
if !settings.isAutomaticProxy && settings.proxy.configurationType == .manual {
|
||||
proxyManualBypassDomains
|
||||
|
@ -272,9 +272,7 @@ extension NetworkSettingsView {
|
|||
|
||||
extension NetworkSettingsView {
|
||||
private var mtuView: some View {
|
||||
Section(
|
||||
header: Text(Unlocalized.Network.mtu)
|
||||
) {
|
||||
Section {
|
||||
Toggle(L10n.Global.Strings.automatic, isOn: $settings.isAutomaticMTU.themeAnimation())
|
||||
|
||||
if !settings.isAutomaticMTU {
|
||||
|
@ -285,6 +283,8 @@ extension NetworkSettingsView {
|
|||
description: \.localizedDescriptionAsMTU
|
||||
)
|
||||
}
|
||||
} header: {
|
||||
Text(Unlocalized.Network.mtu)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,27 +79,27 @@ extension OnDemandView {
|
|||
@ViewBuilder
|
||||
private var mainView: some View {
|
||||
if Utils.hasCellularData() {
|
||||
Section(
|
||||
// TODO: on-demand, restore when "trusted networks" -> "on-demand"
|
||||
// header: Text(L10n.Profile.Sections.Trusted.header)
|
||||
) {
|
||||
Section {
|
||||
Toggle(L10n.OnDemand.Items.Mobile.caption, isOn: $onDemand.withMobileNetwork)
|
||||
} header: {
|
||||
// TODO: on-demand, restore when "trusted networks" -> "on-demand"
|
||||
// Text(L10n.Profile.Sections.Trusted.header)
|
||||
}
|
||||
Section {
|
||||
SSIDList(withSSIDs: $onDemand.withSSIDs)
|
||||
}
|
||||
} else {
|
||||
Section(
|
||||
// TODO: on-demand, restore when "trusted networks" -> "on-demand"
|
||||
// header: Text(L10n.Profile.Sections.Trusted.header)
|
||||
) {
|
||||
Section {
|
||||
SSIDList(withSSIDs: $onDemand.withSSIDs)
|
||||
} header: {
|
||||
// TODO: on-demand, restore when "trusted networks" -> "on-demand"
|
||||
// Text(L10n.Profile.Sections.Trusted.header)
|
||||
}
|
||||
}
|
||||
Section(
|
||||
footer: Text(L10n.OnDemand.Sections.Policy.footer)
|
||||
) {
|
||||
Section {
|
||||
Toggle(L10n.OnDemand.Items.Policy.caption, isOn: $onDemand.disconnectsIfNotMatching)
|
||||
} footer: {
|
||||
Text(L10n.OnDemand.Sections.Policy.footer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -109,16 +109,17 @@ extension PaywallView {
|
|||
}
|
||||
|
||||
private var productsSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Paywall.title),
|
||||
footer: Text(L10n.Paywall.Sections.Products.footer)
|
||||
) {
|
||||
Section {
|
||||
if !productManager.isRefreshingProducts {
|
||||
ForEach(productRowModels, id: \.product.productIdentifier, content: productRow)
|
||||
} else {
|
||||
ProgressView()
|
||||
}
|
||||
restoreRow
|
||||
} header: {
|
||||
Text(L10n.Paywall.title)
|
||||
} footer: {
|
||||
Text(L10n.Paywall.Sections.Products.footer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,9 +49,7 @@ extension ProfileView {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
Section(
|
||||
header: Text(L10n.Global.Strings.configuration)
|
||||
) {
|
||||
Section {
|
||||
if currentProfile.value.vpnProtocols.count > 1 {
|
||||
themeTextPicker(
|
||||
L10n.Global.Strings.protocol,
|
||||
|
@ -109,6 +107,8 @@ extension ProfileView {
|
|||
onDemandRow
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Global.Strings.configuration)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,9 +52,7 @@ extension ProfileView {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
Section(
|
||||
header: Text(L10n.Profile.Sections.Feedback.header)
|
||||
) {
|
||||
Section {
|
||||
if isActiveProfile {
|
||||
NavigationLink {
|
||||
DiagnosticsView(
|
||||
|
@ -70,6 +68,8 @@ extension ProfileView {
|
|||
} label: {
|
||||
Label(Unlocalized.About.faq, systemImage: themeFAQImage)
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.Profile.Sections.Feedback.header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,22 +36,22 @@ extension ProfileView {
|
|||
|
||||
var body: some View {
|
||||
if currentProfile.value.isProvider {
|
||||
Section(
|
||||
footer: Text(L10n.Profile.Sections.VpnResolvesHostname.footer)
|
||||
) {
|
||||
Section {
|
||||
Toggle(
|
||||
L10n.Profile.Items.VpnResolvesHostname.caption,
|
||||
isOn: $currentProfile.value.networkSettings.resolvesHostname
|
||||
)
|
||||
} footer: {
|
||||
Text(L10n.Profile.Sections.VpnResolvesHostname.footer)
|
||||
}
|
||||
}
|
||||
Section(
|
||||
footer: Text(L10n.Profile.Sections.VpnSurvivesSleep.footer)
|
||||
) {
|
||||
Section {
|
||||
Toggle(
|
||||
L10n.Profile.Items.VpnSurvivesSleep.caption,
|
||||
isOn: $currentProfile.value.networkSettings.keepsAliveOnSleep
|
||||
)
|
||||
} footer: {
|
||||
Text(L10n.Profile.Sections.VpnSurvivesSleep.footer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,12 +57,7 @@ extension ProfileView {
|
|||
}
|
||||
|
||||
private var mainView: some View {
|
||||
Section(
|
||||
header: Text(currentProvider.fullName),
|
||||
footer: lastInfrastructureUpdate.map {
|
||||
Text(L10n.Profile.Sections.ProviderInfrastructure.footer($0))
|
||||
}
|
||||
) {
|
||||
Section {
|
||||
NavigationLink(isActive: $isProviderLocationPresented) {
|
||||
ProviderLocationView(
|
||||
currentProfile: currentProfile,
|
||||
|
@ -85,6 +80,12 @@ extension ProfileView {
|
|||
Button(action: refreshInfrastructure) {
|
||||
Text(L10n.Profile.Items.Provider.Refresh.caption)
|
||||
}.withTrailingProgress(when: isRefreshingInfrastructure)
|
||||
} header: {
|
||||
Text(currentProvider.fullName)
|
||||
} footer: {
|
||||
lastInfrastructureUpdate.map {
|
||||
Text(L10n.Profile.Sections.ProviderInfrastructure.footer($0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,12 +45,12 @@ extension ProfileView {
|
|||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(
|
||||
header: Text(L10n.Profile.Alerts.Rename.title)
|
||||
) {
|
||||
Section {
|
||||
TextField(L10n.Global.Placeholders.profileName, text: $newName, onCommit: commitRenaming)
|
||||
.themeValidProfileName()
|
||||
.onAppear(perform: loadCurrentName)
|
||||
} header: {
|
||||
Text(L10n.Profile.Alerts.Rename.title)
|
||||
}
|
||||
}.themeSecondaryView()
|
||||
.navigationTitle(currentProfile.value.header.name)
|
||||
|
|
|
@ -80,11 +80,7 @@ extension ProfileView {
|
|||
}
|
||||
|
||||
private var activeView: some View {
|
||||
Section(
|
||||
header: headerView,
|
||||
footer: Text(L10n.Profile.Sections.Vpn.footer)
|
||||
.xxxThemeTruncation()
|
||||
) {
|
||||
Section {
|
||||
VPNToggle(rateLimit: Constants.RateLimit.vpnToggle) {
|
||||
|
||||
// eligibility: donate intents if eligible for Siri
|
||||
|
@ -105,13 +101,16 @@ extension ProfileView {
|
|||
withErrors: true,
|
||||
dataCountIfAvailable: true
|
||||
))
|
||||
} header: {
|
||||
headerView
|
||||
} footer: {
|
||||
Text(L10n.Profile.Sections.Vpn.footer)
|
||||
.xxxThemeTruncation()
|
||||
}
|
||||
}
|
||||
|
||||
private var inactiveSubview: some View {
|
||||
Section(
|
||||
header: headerView
|
||||
) {
|
||||
Section {
|
||||
Button(L10n.Profile.Items.UseProfile.caption) {
|
||||
Task {
|
||||
|
||||
|
@ -127,14 +126,16 @@ extension ProfileView {
|
|||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
headerView
|
||||
}
|
||||
}
|
||||
|
||||
private var loadingView: some View {
|
||||
Section(
|
||||
header: headerView
|
||||
) {
|
||||
Section {
|
||||
ProgressView()
|
||||
} header: {
|
||||
headerView
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,9 +134,7 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
|
|||
}
|
||||
|
||||
private func categorySection(_ category: ProviderCategory) -> some View {
|
||||
Section(
|
||||
header: !category.name.isEmpty ? Text(category.name) : nil
|
||||
) {
|
||||
Section {
|
||||
ForEach(filteredLocations(for: category)) { location in
|
||||
if isEditable, #available(iOS 15, *) {
|
||||
locationRow(location)
|
||||
|
@ -147,6 +145,8 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
|
|||
locationRow(location)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
!category.name.isEmpty ? Text(category.name) : nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,9 +185,9 @@ struct ProviderLocationView: View, ProviderProfileAvailability {
|
|||
}
|
||||
|
||||
private var emptyFavoritesSection: some View {
|
||||
Section(
|
||||
footer: Text(L10n.Provider.Location.Sections.EmptyFavorites.footer)
|
||||
) {
|
||||
Section {
|
||||
} footer: {
|
||||
Text(L10n.Provider.Location.Sections.EmptyFavorites.footer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,9 +69,7 @@ struct ProviderPresetView: View {
|
|||
}
|
||||
|
||||
private func presetSection(_ preset: ProviderServer.Preset) -> some View {
|
||||
Section(
|
||||
header: Text(preset.name)
|
||||
) {
|
||||
Section {
|
||||
Button {
|
||||
selectedPreset = preset
|
||||
presentationMode.wrappedValue.dismiss()
|
||||
|
@ -89,6 +87,8 @@ struct ProviderPresetView: View {
|
|||
).navigationTitle(preset.name)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(preset.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,24 +49,24 @@ extension ShortcutsView {
|
|||
ZStack {
|
||||
hiddenProviderLocationLink
|
||||
List {
|
||||
Section(
|
||||
header: Text(Unlocalized.VPN.vpn)
|
||||
) {
|
||||
Section {
|
||||
addConnectView
|
||||
Button(L10n.Shortcuts.Add.Items.EnableVpn.caption, action: addEnableVPN)
|
||||
Button(L10n.Shortcuts.Add.Items.DisableVpn.caption, action: addDisableVPN)
|
||||
} header: {
|
||||
Text(Unlocalized.VPN.vpn)
|
||||
}
|
||||
Section(
|
||||
header: Text(L10n.Shortcuts.Add.Sections.Wifi.header)
|
||||
) {
|
||||
Section {
|
||||
Button(L10n.Shortcuts.Add.Items.TrustCurrentWifi.caption, action: addTrustWiFi)
|
||||
Button(L10n.Shortcuts.Add.Items.UntrustCurrentWifi.caption, action: addUntrustWiFi)
|
||||
} header: {
|
||||
Text(L10n.Shortcuts.Add.Sections.Wifi.header)
|
||||
}
|
||||
Section(
|
||||
header: Text(L10n.Shortcuts.Add.Sections.Cellular.header)
|
||||
) {
|
||||
Section {
|
||||
Button(L10n.Shortcuts.Add.Items.TrustCellular.caption, action: addTrustCellular)
|
||||
Button(L10n.Shortcuts.Add.Items.UntrustCellular.caption, action: addUntrustCellular)
|
||||
} header: {
|
||||
Text(L10n.Shortcuts.Add.Sections.Cellular.header)
|
||||
}
|
||||
}
|
||||
}.navigationTitle(L10n.Shortcuts.Add.title)
|
||||
|
|
|
@ -85,10 +85,10 @@ struct ShortcutsView: View {
|
|||
}
|
||||
|
||||
private var shortcutsSection: some View {
|
||||
Section(
|
||||
header: Text(L10n.Shortcuts.Edit.Sections.All.header)
|
||||
) {
|
||||
Section {
|
||||
ForEach(relevantShortcuts, content: rowView)
|
||||
} header: {
|
||||
Text(L10n.Shortcuts.Edit.Sections.All.header)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,9 +99,7 @@ struct ShortcutsView: View {
|
|||
}
|
||||
|
||||
private var addSection: some View {
|
||||
Section(
|
||||
footer: Text(L10n.Shortcuts.Edit.Sections.Add.footer)
|
||||
) {
|
||||
Section {
|
||||
NavigationLink(isActive: $isNavigationPresented) {
|
||||
AddView(
|
||||
target: target,
|
||||
|
@ -110,6 +108,8 @@ struct ShortcutsView: View {
|
|||
} label: {
|
||||
Text(L10n.Shortcuts.Edit.Items.AddShortcut.caption)
|
||||
}
|
||||
} footer: {
|
||||
Text(L10n.Shortcuts.Edit.Sections.Add.footer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue