Improve some things about providers (#757)
- iOS: Add category name to clarify servers context - iOS: Show "No servers" when list is empty - macOS: Show "Connect" in server selector when presenting from home - Add last update to issue report - Refactor provider strings
This commit is contained in:
parent
2c2b3f063a
commit
3abde3851a
|
@ -41,7 +41,7 @@
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "7b3f68b30d2c3ac434c5aaa10853bd3709c5c308"
|
"revision" : "3934b7a4e64624d499f0d52d9053560554bd4be8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -28,7 +28,7 @@ let package = Package(
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.0"),
|
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.0"),
|
||||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "7b3f68b30d2c3ac434c5aaa10853bd3709c5c308"),
|
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "3934b7a4e64624d499f0d52d9053560554bd4be8"),
|
||||||
// .package(path: "../../../passepartoutkit-source"),
|
// .package(path: "../../../passepartoutkit-source"),
|
||||||
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.9.1"),
|
.package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", from: "0.9.1"),
|
||||||
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
|
// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source-openvpn-openssl", revision: "031863a1cd683962a7dfe68e20b91fa820a1ecce"),
|
||||||
|
|
|
@ -31,6 +31,8 @@ extension Issue {
|
||||||
struct Metadata {
|
struct Metadata {
|
||||||
let profile: Profile?
|
let profile: Profile?
|
||||||
|
|
||||||
|
let provider: (ProviderID, Date?)?
|
||||||
|
|
||||||
let configuration: PassepartoutConfiguration
|
let configuration: PassepartoutConfiguration
|
||||||
|
|
||||||
let versionString: String
|
let versionString: String
|
||||||
|
@ -73,7 +75,7 @@ extension Issue {
|
||||||
purchasedProducts: metadata.purchasedProducts,
|
purchasedProducts: metadata.purchasedProducts,
|
||||||
appLog: appLog,
|
appLog: appLog,
|
||||||
tunnelLog: tunnelLog,
|
tunnelLog: tunnelLog,
|
||||||
providerId: metadata.profile?.firstProviderModuleWithMetadata?.1.id
|
provider: metadata.provider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,12 +52,14 @@ struct Issue: Identifiable {
|
||||||
|
|
||||||
let providerName: String?
|
let providerName: String?
|
||||||
|
|
||||||
|
let providerLastUpdate: Date?
|
||||||
|
|
||||||
init(
|
init(
|
||||||
appLine: String?,
|
appLine: String?,
|
||||||
purchasedProducts: Set<AppProduct>,
|
purchasedProducts: Set<AppProduct>,
|
||||||
appLog: Data? = nil,
|
appLog: Data? = nil,
|
||||||
tunnelLog: Data? = nil,
|
tunnelLog: Data? = nil,
|
||||||
providerId: ProviderID? = nil
|
provider: (ProviderID, Date?)? = nil
|
||||||
) {
|
) {
|
||||||
id = UUID()
|
id = UUID()
|
||||||
self.appLine = appLine
|
self.appLine = appLine
|
||||||
|
@ -85,7 +87,8 @@ struct Issue: Identifiable {
|
||||||
osLine = "\(osName) \(osVersion)"
|
osLine = "\(osName) \(osVersion)"
|
||||||
deviceLine = deviceType
|
deviceLine = deviceType
|
||||||
|
|
||||||
providerName = providerId?.rawValue
|
providerName = provider?.0.rawValue
|
||||||
|
providerLastUpdate = provider?.1
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: String {
|
var body: String {
|
||||||
|
@ -94,7 +97,7 @@ struct Issue: Identifiable {
|
||||||
.replacingOccurrences(of: "$osLine", with: osLine)
|
.replacingOccurrences(of: "$osLine", with: osLine)
|
||||||
.replacingOccurrences(of: "$deviceLine", with: deviceLine ?? "unknown")
|
.replacingOccurrences(of: "$deviceLine", with: deviceLine ?? "unknown")
|
||||||
.replacingOccurrences(of: "$providerName", with: providerName ?? "none")
|
.replacingOccurrences(of: "$providerName", with: providerName ?? "none")
|
||||||
.replacingOccurrences(of: "$providerLastUpdate", with: "unknown")
|
.replacingOccurrences(of: "$providerLastUpdate", with: providerLastUpdate?.timestamp ?? "unknown")
|
||||||
.replacingOccurrences(of: "$purchasedProducts", with: purchasedProducts.map(\.rawValue).description)
|
.replacingOccurrences(of: "$purchasedProducts", with: purchasedProducts.map(\.rawValue).description)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -452,6 +452,36 @@ public enum Strings {
|
||||||
public static let name = Strings.tr("Localizable", "placeholders.profile.name", fallback: "My profile")
|
public static let name = Strings.tr("Localizable", "placeholders.profile.name", fallback: "My profile")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public enum Providers {
|
||||||
|
/// Clear filters
|
||||||
|
public static let clearFilters = Strings.tr("Localizable", "providers.clear_filters", fallback: "Clear filters")
|
||||||
|
/// Last updated on %@
|
||||||
|
public static func lastUpdated(_ p1: Any) -> String {
|
||||||
|
return Strings.tr("Localizable", "providers.last_updated", String(describing: p1), fallback: "Last updated on %@")
|
||||||
|
}
|
||||||
|
/// None
|
||||||
|
public static let noProvider = Strings.tr("Localizable", "providers.no_provider", fallback: "None")
|
||||||
|
/// Refresh infrastructure
|
||||||
|
public static let refreshInfrastructure = Strings.tr("Localizable", "providers.refresh_infrastructure", fallback: "Refresh infrastructure")
|
||||||
|
/// Select
|
||||||
|
public static let selectEntity = Strings.tr("Localizable", "providers.select_entity", fallback: "Select")
|
||||||
|
/// Select a provider
|
||||||
|
public static let selectProvider = Strings.tr("Localizable", "providers.select_provider", fallback: "Select a provider")
|
||||||
|
public enum LastUpdated {
|
||||||
|
/// Loading...
|
||||||
|
public static let loading = Strings.tr("Localizable", "providers.last_updated.loading", fallback: "Loading...")
|
||||||
|
}
|
||||||
|
public enum Vpn {
|
||||||
|
/// No servers
|
||||||
|
public static let noServers = Strings.tr("Localizable", "providers.vpn.no_servers", fallback: "No servers")
|
||||||
|
/// Preset
|
||||||
|
public static let preset = Strings.tr("Localizable", "providers.vpn.preset", fallback: "Preset")
|
||||||
|
public enum Category {
|
||||||
|
/// All categories
|
||||||
|
public static let any = Strings.tr("Localizable", "providers.vpn.category.any", fallback: "All categories")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
public enum Theme {
|
public enum Theme {
|
||||||
public enum Confirmation {
|
public enum Confirmation {
|
||||||
/// Are you sure?
|
/// Are you sure?
|
||||||
|
@ -609,30 +639,6 @@ public enum Strings {
|
||||||
public static let newProfile = Strings.tr("Localizable", "views.profiles.toolbar.new_profile", fallback: "New profile")
|
public static let newProfile = Strings.tr("Localizable", "views.profiles.toolbar.new_profile", fallback: "New profile")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public enum Provider {
|
|
||||||
/// Clear filters
|
|
||||||
public static let clearFilters = Strings.tr("Localizable", "views.provider.clear_filters", fallback: "Clear filters")
|
|
||||||
/// None
|
|
||||||
public static let noProvider = Strings.tr("Localizable", "views.provider.no_provider", fallback: "None")
|
|
||||||
/// Select a provider
|
|
||||||
public static let selectProvider = Strings.tr("Localizable", "views.provider.select_provider", fallback: "Select a provider")
|
|
||||||
/// Select
|
|
||||||
public static let selectServer = Strings.tr("Localizable", "views.provider.select_server", fallback: "Select")
|
|
||||||
public enum Vpn {
|
|
||||||
/// Last updated on %@
|
|
||||||
public static func lastUpdated(_ p1: Any) -> String {
|
|
||||||
return Strings.tr("Localizable", "views.provider.vpn.last_updated", String(describing: p1), fallback: "Last updated on %@")
|
|
||||||
}
|
|
||||||
/// Preset
|
|
||||||
public static let preset = Strings.tr("Localizable", "views.provider.vpn.preset", fallback: "Preset")
|
|
||||||
/// Refresh infrastructure
|
|
||||||
public static let refreshInfrastructure = Strings.tr("Localizable", "views.provider.vpn.refresh_infrastructure", fallback: "Refresh infrastructure")
|
|
||||||
public enum LastUpdated {
|
|
||||||
/// Loading...
|
|
||||||
public static let loading = Strings.tr("Localizable", "views.provider.vpn.last_updated.loading", fallback: "Loading...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public enum Settings {
|
public enum Settings {
|
||||||
public enum Rows {
|
public enum Rows {
|
||||||
/// Ask before quit
|
/// Ask before quit
|
||||||
|
|
|
@ -133,15 +133,6 @@
|
||||||
"views.profile.rows.add_module" = "Add module";
|
"views.profile.rows.add_module" = "Add module";
|
||||||
"views.profile.module_list.section.footer" = "Drag modules to rearrange them, as their order determines priority.";
|
"views.profile.module_list.section.footer" = "Drag modules to rearrange them, as their order determines priority.";
|
||||||
|
|
||||||
"views.provider.no_provider" = "None";
|
|
||||||
"views.provider.select_provider" = "Select a provider";
|
|
||||||
"views.provider.clear_filters" = "Clear filters";
|
|
||||||
"views.provider.select_server" = "Select";
|
|
||||||
"views.provider.vpn.refresh_infrastructure" = "Refresh infrastructure";
|
|
||||||
"views.provider.vpn.last_updated" = "Last updated on %@";
|
|
||||||
"views.provider.vpn.last_updated.loading" = "Loading...";
|
|
||||||
"views.provider.vpn.preset" = "Preset";
|
|
||||||
|
|
||||||
"views.settings.sections.icloud.footer" = "To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles.";
|
"views.settings.sections.icloud.footer" = "To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles.";
|
||||||
"views.settings.rows.confirm_quit" = "Ask before quit";
|
"views.settings.rows.confirm_quit" = "Ask before quit";
|
||||||
"views.settings.rows.lock_in_background" = "Lock in background";
|
"views.settings.rows.lock_in_background" = "Lock in background";
|
||||||
|
@ -232,6 +223,19 @@
|
||||||
"modules.wireguard.preshared_key" = "Pre-shared key";
|
"modules.wireguard.preshared_key" = "Pre-shared key";
|
||||||
"modules.wireguard.allowed_ips" = "Allowed IPs";
|
"modules.wireguard.allowed_ips" = "Allowed IPs";
|
||||||
|
|
||||||
|
// MARK: - Providers
|
||||||
|
|
||||||
|
"providers.no_provider" = "None";
|
||||||
|
"providers.select_provider" = "Select a provider";
|
||||||
|
"providers.select_entity" = "Select";
|
||||||
|
"providers.clear_filters" = "Clear filters";
|
||||||
|
"providers.refresh_infrastructure" = "Refresh infrastructure";
|
||||||
|
"providers.last_updated" = "Last updated on %@";
|
||||||
|
"providers.last_updated.loading" = "Loading...";
|
||||||
|
"providers.vpn.category.any" = "All categories";
|
||||||
|
"providers.vpn.preset" = "Preset";
|
||||||
|
"providers.vpn.no_servers" = "No servers";
|
||||||
|
|
||||||
// MARK: - Components
|
// MARK: - Components
|
||||||
|
|
||||||
"ui.connection_status.on_demand_suffix" = " (on-demand)";
|
"ui.connection_status.on_demand_suffix" = " (on-demand)";
|
||||||
|
|
|
@ -32,6 +32,9 @@ struct ReportIssueButton {
|
||||||
@EnvironmentObject
|
@EnvironmentObject
|
||||||
private var profileManager: ProfileManager
|
private var profileManager: ProfileManager
|
||||||
|
|
||||||
|
@EnvironmentObject
|
||||||
|
private var providerManager: ProviderManager
|
||||||
|
|
||||||
let tunnel: Tunnel
|
let tunnel: Tunnel
|
||||||
|
|
||||||
let title: String
|
let title: String
|
||||||
|
@ -53,4 +56,12 @@ struct ReportIssueButton {
|
||||||
}
|
}
|
||||||
return profileManager.profile(withId: id)
|
return profileManager.profile(withId: id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var currentProvider: (ProviderID, Date?)? {
|
||||||
|
guard let id = installedProfile?.firstProviderModuleWithMetadata?.1.id else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let lastUpdate = providerManager.lastUpdated(for: id)
|
||||||
|
return (id, lastUpdate)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,7 @@ private extension ReportIssueButton {
|
||||||
}
|
}
|
||||||
let issue = await Issue.withMetadata(.init(
|
let issue = await Issue.withMetadata(.init(
|
||||||
profile: installedProfile,
|
profile: installedProfile,
|
||||||
|
provider: currentProvider,
|
||||||
configuration: .shared,
|
configuration: .shared,
|
||||||
versionString: BundleConfiguration.mainVersionString,
|
versionString: BundleConfiguration.mainVersionString,
|
||||||
purchasedProducts: purchasedProducts,
|
purchasedProducts: purchasedProducts,
|
||||||
|
|
|
@ -51,6 +51,7 @@ private extension ReportIssueButton {
|
||||||
}
|
}
|
||||||
let issue = await Issue.withMetadata(.init(
|
let issue = await Issue.withMetadata(.init(
|
||||||
profile: installedProfile,
|
profile: installedProfile,
|
||||||
|
provider: currentProvider,
|
||||||
configuration: .shared,
|
configuration: .shared,
|
||||||
versionString: BundleConfiguration.mainVersionString,
|
versionString: BundleConfiguration.mainVersionString,
|
||||||
purchasedProducts: purchasedProducts,
|
purchasedProducts: purchasedProducts,
|
||||||
|
|
|
@ -132,6 +132,7 @@ private extension OpenVPNView {
|
||||||
configurationType: OpenVPN.Configuration.self,
|
configurationType: OpenVPN.Configuration.self,
|
||||||
selectedEntity: providerEntity.wrappedValue,
|
selectedEntity: providerEntity.wrappedValue,
|
||||||
filtersWithSelection: true,
|
filtersWithSelection: true,
|
||||||
|
selectTitle: Strings.Providers.selectEntity,
|
||||||
onSelect: onSelectServer
|
onSelect: onSelectServer
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ private extension ProviderContentModifier {
|
||||||
providerRows
|
providerRows
|
||||||
refreshButton {
|
refreshButton {
|
||||||
HStack {
|
HStack {
|
||||||
Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
|
Text(Strings.Providers.refreshInfrastructure)
|
||||||
if providerManager.isLoading {
|
if providerManager.isLoading {
|
||||||
Spacer()
|
Spacer()
|
||||||
ProgressView()
|
ProgressView()
|
||||||
|
@ -102,7 +102,7 @@ private extension ProviderContentModifier {
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
refreshButton {
|
refreshButton {
|
||||||
Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
|
Text(Strings.Providers.refreshInfrastructure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,9 +138,9 @@ private extension ProviderContentModifier {
|
||||||
|
|
||||||
var lastUpdatedString: String? {
|
var lastUpdatedString: String? {
|
||||||
guard let lastUpdated else {
|
guard let lastUpdated else {
|
||||||
return providerManager.isLoading ? Strings.Views.Provider.Vpn.LastUpdated.loading : nil
|
return providerManager.isLoading ? Strings.Providers.LastUpdated.loading : nil
|
||||||
}
|
}
|
||||||
return Strings.Views.Provider.Vpn.lastUpdated(lastUpdated.timestamp)
|
return Strings.Providers.lastUpdated(lastUpdated.timestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ struct ProviderPicker: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Picker(Strings.Global.provider, selection: $providerId) {
|
Picker(Strings.Global.provider, selection: $providerId) {
|
||||||
if !providers.isEmpty {
|
if !providers.isEmpty {
|
||||||
Text(isRequired ? Strings.Views.Provider.selectProvider : Strings.Views.Provider.noProvider)
|
Text(isRequired ? Strings.Providers.selectProvider : Strings.Providers.noProvider)
|
||||||
.tag(nil as ProviderID?)
|
.tag(nil as ProviderID?)
|
||||||
ForEach(providers, id: \.id) {
|
ForEach(providers, id: \.id) {
|
||||||
Text($0.description)
|
Text($0.description)
|
||||||
|
|
|
@ -134,7 +134,7 @@ private extension VPNFiltersView.Subview {
|
||||||
}
|
}
|
||||||
|
|
||||||
var presetPicker: some View {
|
var presetPicker: some View {
|
||||||
Picker(Strings.Views.Provider.Vpn.preset, selection: $filters.presetId) {
|
Picker(Strings.Providers.Vpn.preset, selection: $filters.presetId) {
|
||||||
Text(Strings.Global.any)
|
Text(Strings.Global.any)
|
||||||
.tag(nil as String?)
|
.tag(nil as String?)
|
||||||
ForEach(presets, id: \.presetId) {
|
ForEach(presets, id: \.presetId) {
|
||||||
|
@ -145,7 +145,7 @@ private extension VPNFiltersView.Subview {
|
||||||
}
|
}
|
||||||
|
|
||||||
var clearFiltersButton: some View {
|
var clearFiltersButton: some View {
|
||||||
Button(Strings.Views.Provider.clearFilters, role: .destructive) {
|
Button(Strings.Providers.clearFilters, role: .destructive) {
|
||||||
filters = VPNFilters()
|
filters = VPNFilters()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,7 @@ struct VPNProviderServerCoordinator<Configuration>: View where Configuration: Pr
|
||||||
configurationType: Configuration.self,
|
configurationType: Configuration.self,
|
||||||
selectedEntity: selectedEntity,
|
selectedEntity: selectedEntity,
|
||||||
filtersWithSelection: false,
|
filtersWithSelection: false,
|
||||||
|
selectTitle: Strings.Global.connect,
|
||||||
onSelect: onSelect
|
onSelect: onSelect
|
||||||
)
|
)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
|
|
@ -43,6 +43,8 @@ struct VPNProviderServerView<Configuration>: View where Configuration: ProviderC
|
||||||
|
|
||||||
let filtersWithSelection: Bool
|
let filtersWithSelection: Bool
|
||||||
|
|
||||||
|
let selectTitle: String
|
||||||
|
|
||||||
let onSelect: (_ server: VPNServer, _ preset: VPNPreset<Configuration>) -> Void
|
let onSelect: (_ server: VPNServer, _ preset: VPNPreset<Configuration>) -> Void
|
||||||
|
|
||||||
@StateObject
|
@StateObject
|
||||||
|
@ -64,10 +66,12 @@ struct VPNProviderServerView<Configuration>: View where Configuration: ProviderC
|
||||||
manager: manager,
|
manager: manager,
|
||||||
selectedServer: selectedEntity?.server,
|
selectedServer: selectedEntity?.server,
|
||||||
filters: $filters,
|
filters: $filters,
|
||||||
|
selectTitle: selectTitle,
|
||||||
onSelect: selectServer
|
onSelect: selectServer
|
||||||
)
|
)
|
||||||
.withErrorHandler(errorHandler)
|
.withErrorHandler(errorHandler)
|
||||||
.navigationTitle(Strings.Global.servers)
|
.navigationTitle(Strings.Global.servers)
|
||||||
|
.themeNavigationDetail()
|
||||||
.onLoad {
|
.onLoad {
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
|
@ -124,6 +128,7 @@ extension VPNProviderServerView {
|
||||||
configurationType: OpenVPN.Configuration.self,
|
configurationType: OpenVPN.Configuration.self,
|
||||||
selectedEntity: nil,
|
selectedEntity: nil,
|
||||||
filtersWithSelection: false,
|
filtersWithSelection: false,
|
||||||
|
selectTitle: "Select",
|
||||||
onSelect: { _, _ in }
|
onSelect: { _, _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,9 @@ extension VPNProviderServerView {
|
||||||
@Binding
|
@Binding
|
||||||
var filters: VPNFilters
|
var filters: VPNFilters
|
||||||
|
|
||||||
|
// unused
|
||||||
|
let selectTitle: String
|
||||||
|
|
||||||
let onSelect: (VPNServer) -> Void
|
let onSelect: (VPNServer) -> Void
|
||||||
|
|
||||||
@State
|
@State
|
||||||
|
@ -56,11 +59,20 @@ extension VPNProviderServerView {
|
||||||
|
|
||||||
private extension VPNProviderServerView.Subview {
|
private extension VPNProviderServerView.Subview {
|
||||||
var listView: some View {
|
var listView: some View {
|
||||||
List {
|
ZStack {
|
||||||
if manager.isFiltering {
|
if manager.isFiltering {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
|
} else if !manager.filteredServers.isEmpty {
|
||||||
|
List {
|
||||||
|
Section {
|
||||||
|
ForEach(countryCodes, id: \.self, content: countryView)
|
||||||
|
} header: {
|
||||||
|
Text(filters.categoryName ?? Strings.Providers.Vpn.Category.any)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ForEach(countryCodes, id: \.self, content: countryView)
|
Text(Strings.Providers.Vpn.noServers)
|
||||||
|
.themeEmptyMessage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.themeAnimation(on: manager.isFiltering, category: .providers)
|
.themeAnimation(on: manager.isFiltering, category: .providers)
|
||||||
|
@ -144,6 +156,7 @@ private extension VPNProviderServerView.Subview {
|
||||||
configurationType: OpenVPN.Configuration.self,
|
configurationType: OpenVPN.Configuration.self,
|
||||||
selectedEntity: nil,
|
selectedEntity: nil,
|
||||||
filtersWithSelection: false,
|
filtersWithSelection: false,
|
||||||
|
selectTitle: "Select",
|
||||||
onSelect: { _, _ in }
|
onSelect: { _, _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ extension VPNProviderServerView {
|
||||||
@Binding
|
@Binding
|
||||||
var filters: VPNFilters
|
var filters: VPNFilters
|
||||||
|
|
||||||
|
let selectTitle: String
|
||||||
|
|
||||||
let onSelect: (VPNServer) -> Void
|
let onSelect: (VPNServer) -> Void
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -72,7 +74,7 @@ private extension VPNProviderServerView.Subview {
|
||||||
Button {
|
Button {
|
||||||
onSelect(server)
|
onSelect(server)
|
||||||
} label: {
|
} label: {
|
||||||
Text(Strings.Views.Provider.selectServer)
|
Text(selectTitle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +100,7 @@ private extension VPNProviderServerView.Subview {
|
||||||
configurationType: OpenVPN.Configuration.self,
|
configurationType: OpenVPN.Configuration.self,
|
||||||
selectedEntity: nil,
|
selectedEntity: nil,
|
||||||
filtersWithSelection: false,
|
filtersWithSelection: false,
|
||||||
|
selectTitle: "Select",
|
||||||
onSelect: { _, _ in }
|
onSelect: { _, _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue