Fix some key things about providers UI (#736)

Fixes #703
This commit is contained in:
Davide 2024-10-16 08:53:16 +02:00 committed by GitHub
parent 9656e5ed29
commit acf066571a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 80 additions and 45 deletions

View File

@ -81,7 +81,7 @@ struct Issue: Identifiable {
.replacingOccurrences(of: "$appLine", with: appLine ?? "unknown")
.replacingOccurrences(of: "$osLine", with: osLine)
.replacingOccurrences(of: "$deviceLine", with: deviceLine ?? "unknown")
// FIXME: #703, report provider in issue
// FIXME: #710, report provider in issue
.replacingOccurrences(of: "$providerName", with: "none")
.replacingOccurrences(of: "$providerLastUpdate", with: "unknown")
.replacingOccurrences(of: "$purchasedProducts", with: purchasedProducts.map(\.rawValue).description)

View File

@ -73,6 +73,9 @@ extension PassepartoutError: LocalizedError {
case .parsing:
return reason?.localizedDescription ?? Strings.Errors.App.Passepartout.parsing
case .corruptProviderModule:
return Strings.Errors.App.Passepartout.corruptProviderModule(reason?.localizedDescription ?? "")
case .unhandled:
return reason?.localizedDescription

View File

@ -113,6 +113,10 @@ public enum Strings {
/// Only one connection module can be active at a time.
public static let multipleConnectionModules = Strings.tr("Localizable", "errors.app.multiple_connection_modules", fallback: "Only one connection module can be active at a time.")
public enum Passepartout {
/// Unable to connect to provider server (reason=%@).
public static func corruptProviderModule(_ p1: Any) -> String {
return Strings.tr("Localizable", "errors.app.passepartout.corrupt_provider_module", String(describing: p1), fallback: "Unable to connect to provider server (reason=%@).")
}
/// Unable to complete operation (code=%@).
public static func `default`(_ p1: Any) -> String {
return Strings.tr("Localizable", "errors.app.passepartout.default", String(describing: p1), fallback: "Unable to complete operation (code=%@).")
@ -159,6 +163,8 @@ public enum Strings {
public static let any = Strings.tr("Localizable", "global.any", fallback: "Any")
/// Cancel
public static let cancel = Strings.tr("Localizable", "global.cancel", fallback: "Cancel")
/// Category
public static let category = Strings.tr("Localizable", "global.category", fallback: "Category")
/// Certificate
public static let certificate = Strings.tr("Localizable", "global.certificate", fallback: "Certificate")
/// Compression
@ -167,6 +173,8 @@ public enum Strings {
public static let connect = Strings.tr("Localizable", "global.connect", fallback: "Connect")
/// Connection
public static let connection = Strings.tr("Localizable", "global.connection", fallback: "Connection")
/// Country
public static let country = Strings.tr("Localizable", "global.country", fallback: "Country")
/// Default
public static let `default` = Strings.tr("Localizable", "global.default", fallback: "Default")
/// Destination
@ -195,6 +203,8 @@ public enum Strings {
public static let enabled = Strings.tr("Localizable", "global.enabled", fallback: "Enabled")
/// Endpoint
public static let endpoint = Strings.tr("Localizable", "global.endpoint", fallback: "Endpoint")
/// Filters
public static let filters = Strings.tr("Localizable", "global.filters", fallback: "Filters")
/// Folder
public static let folder = Strings.tr("Localizable", "global.folder", fallback: "Folder")
/// Gateway
@ -251,6 +261,8 @@ public enum Strings {
public static let publicKey = Strings.tr("Localizable", "global.public_key", fallback: "Public key")
/// Purchase
public static let purchase = Strings.tr("Localizable", "global.purchase", fallback: "Purchase")
/// Region
public static let region = Strings.tr("Localizable", "global.region", fallback: "Region")
/// Delete
public static let remove = Strings.tr("Localizable", "global.remove", fallback: "Delete")
/// Restart
@ -590,19 +602,27 @@ public enum Strings {
}
}
public enum Provider {
/// Last updated on %@
public static func lastUpdated(_ p1: Any) -> String {
return Strings.tr("Localizable", "views.provider.last_updated", String(describing: p1), fallback: "Last updated on %@")
}
/// 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")
/// Refresh infrastructure
public static let refreshInfrastructure = Strings.tr("Localizable", "views.provider.refresh_infrastructure", fallback: "Refresh infrastructure")
/// Select a provider
public static let selectProvider = Strings.tr("Localizable", "views.provider.select_provider", fallback: "Select a provider")
public enum LastUpdated {
/// Loading...
public static let loading = Strings.tr("Localizable", "views.provider.last_updated.loading", fallback: "Loading...")
/// 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 {

View File

@ -6,10 +6,12 @@
"global.addresses" = "Addresses";
"global.any" = "Any";
"global.cancel" = "Cancel";
"global.category" = "Category";
"global.certificate" = "Certificate";
"global.compression" = "Compression";
"global.connect" = "Connect";
"global.connection" = "Connection";
"global.country" = "Country";
"global.default" = "Default";
"global.destination" = "Destination";
"global.disable" = "Disable";
@ -24,6 +26,7 @@
"global.enable" = "Enable";
"global.enabled" = "Enabled";
"global.endpoint" = "Endpoint";
"global.filters" = "Filters";
"global.folder" = "Folder";
"global.gateway" = "Gateway";
"global.general" = "General";
@ -51,6 +54,7 @@
"global.provider" = "Provider";
"global.public_key" = "Public key";
"global.purchase" = "Purchase";
"global.region" = "Region";
"global.remove" = "Delete";
"global.restart" = "Restart";
"global.route" = "Route";
@ -131,9 +135,12 @@
"views.provider.no_provider" = "None";
"views.provider.select_provider" = "Select a provider";
"views.provider.refresh_infrastructure" = "Refresh infrastructure";
"views.provider.last_updated" = "Last updated on %@";
"views.provider.last_updated.loading" = "Loading...";
"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.rows.confirm_quit" = "Ask before quit";
@ -246,6 +253,7 @@
"errors.app.default" = "Unable to complete operation.";
"errors.app.passepartout.invalid_fields" = "Invalid fields (%@).";
"errors.app.passepartout.parsing" = "Unable to parse file.";
"errors.app.passepartout.corrupt_provider_module" = "Unable to connect to provider server (reason=%@).";
"errors.app.passepartout.default" = "Unable to complete operation (code=%@).";
"errors.tunnel.auth" = "Auth failed";

View File

@ -78,7 +78,7 @@ private extension ProviderContentModifier {
providerRows
refreshButton {
HStack {
Text(Strings.Views.Provider.refreshInfrastructure)
Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
if providerManager.isLoading {
Spacer()
ProgressView()
@ -102,7 +102,7 @@ private extension ProviderContentModifier {
}
Spacer()
refreshButton {
Text(Strings.Views.Provider.refreshInfrastructure)
Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
}
}
}
@ -141,9 +141,9 @@ private extension ProviderContentModifier {
var lastUpdatedString: String? {
guard let lastUpdated else {
return providerManager.isLoading ? Strings.Views.Provider.LastUpdated.loading : nil
return providerManager.isLoading ? Strings.Views.Provider.Vpn.LastUpdated.loading : nil
}
return Strings.Views.Provider.lastUpdated(lastUpdated.timestamp)
return Strings.Views.Provider.Vpn.lastUpdated(lastUpdated.timestamp)
}
}

View File

@ -26,6 +26,8 @@
import PassepartoutKit
import SwiftUI
// FIXME: ###, providers UI, iPadOS (Simulator?) picker .navigationLink selection is blue (vs gray) and disclosed options are white
struct ProviderPicker: View {
let providers: [ProviderMetadata]

View File

@ -27,8 +27,6 @@ import AppLibrary
import PassepartoutKit
import SwiftUI
// FIXME: #703, providers UI
struct VPNFiltersView<Configuration>: View where Configuration: Decodable {
@ObservedObject
@ -56,8 +54,8 @@ struct VPNFiltersView<Configuration>: View where Configuration: Decodable {
private extension VPNFiltersView {
var categoryPicker: some View {
Picker("Category", selection: $manager.parameters.filters.categoryName) {
Text("Any")
Picker(Strings.Global.category, selection: $manager.parameters.filters.categoryName) {
Text(Strings.Global.any)
.tag(nil as String?)
ForEach(categories, id: \.self) {
Text($0.capitalized)
@ -67,8 +65,8 @@ private extension VPNFiltersView {
}
var countryPicker: some View {
Picker("Country", selection: $manager.parameters.filters.countryCode) {
Text("Any")
Picker(Strings.Global.country, selection: $manager.parameters.filters.countryCode) {
Text(Strings.Global.any)
.tag(nil as String?)
ForEach(countries, id: \.code) {
Text($0.description)
@ -80,8 +78,8 @@ private extension VPNFiltersView {
@ViewBuilder
var presetPicker: some View {
if manager.allPresets.count > 1 {
Picker("Preset", selection: $manager.parameters.filters.presetId) {
Text("Any")
Picker(Strings.Views.Provider.Vpn.preset, selection: $manager.parameters.filters.presetId) {
Text(Strings.Global.any)
.tag(nil as String?)
ForEach(presets, id: \.presetId) {
Text($0.description)
@ -92,7 +90,7 @@ private extension VPNFiltersView {
}
var clearFiltersButton: some View {
Button("Clear filters", role: .destructive) {
Button(Strings.Views.Provider.clearFilters, role: .destructive) {
Task {
manager.resetFilters()
}

View File

@ -49,7 +49,8 @@ struct VPNProviderServerView<Configuration>: View where Configuration: ProviderC
extension VPNProviderServerView {
func selectServer(_ server: VPNServer) {
guard let preset = compatiblePreset(with: server) else {
// FIXME: #703, alert select a preset
pp_log(.app, .error, "Unable to find a compatible preset. Supported IDs: \(server.provider.supportedPresetIds ?? [])")
assertionFailure("No compatible presets for server \(server.serverId) (manager=\(manager.providerId), configuration=\(Configuration.providerConfigurationIdentifier), supported=\(server.provider.supportedPresetIds ?? []))")
return
}
onSelect(server, preset)

View File

@ -27,24 +27,26 @@
import SwiftUI
// FIXME: #703, providers UI
// FIXME: ###, providers UI, iPadOS show filters in popover
extension VPNFiltersModifier {
func contentView(with content: Content) -> some View {
content
.toolbar {
Button {
isFiltersPresented = true
} label: {
Image(systemName: "line.3.horizontal.decrease")
}
.themeModal(isPresented: $isFiltersPresented) {
NavigationStack {
VPNFiltersView<Configuration>(manager: manager)
.navigationTitle("Filters")
.navigationBarTitleDisplayMode(.inline)
ToolbarItem(placement: .bottomBar) {
Button {
isFiltersPresented = true
} label: {
ThemeImage(.filters)
}
.themeModal(isPresented: $isFiltersPresented) {
NavigationStack {
VPNFiltersView<Configuration>(manager: manager)
.navigationTitle(Strings.Global.filters)
.navigationBarTitleDisplayMode(.inline)
}
.presentationDetents([.medium])
}
.presentationDetents([.medium])
}
}
}

View File

@ -27,7 +27,7 @@
import SwiftUI
// FIXME: #703, providers UI
// FIXME: ###, providers UI, iOS server rows + country flags
extension VPNProviderServerView {

View File

@ -27,25 +27,25 @@
import SwiftUI
// FIXME: #703, providers UI
// FIXME: ###, providers UI, macOS country flags
extension VPNProviderServerView {
@ViewBuilder
var serversView: some View {
Table(manager.filteredServers) {
TableColumn("Region") { server in
TableColumn(Strings.Global.region) { server in
Text(server.region)
}
.width(max: 200.0)
TableColumn("Address", value: \.address)
TableColumn(Strings.Global.address, value: \.address)
TableColumn("") { server in
Button {
selectServer(server)
} label: {
Text("Select")
Text(Strings.Views.Provider.selectServer)
}
}
.width(min: 100.0, max: 100.0)

View File

@ -36,6 +36,7 @@ extension Theme {
case disclose
case editableSectionEdit
case editableSectionRemove
case filters
case footerAdd
case hide
case info

View File

@ -87,6 +87,7 @@ public final class Theme: ObservableObject {
case .disclose: return "chevron.down"
case .editableSectionEdit: return "arrow.up.arrow.down"
case .editableSectionRemove: return "trash"
case .filters: return "line.3.horizontal.decrease"
case .footerAdd: return "plus.circle"
case .hide: return "eye.slash"
case .info: return "info.circle"

View File

@ -141,7 +141,6 @@ extension ProfileProcessor {
_ = try profile.withProviderModules()
return profile
} catch {
// FIXME: #703, alert unable to build provider server
pp_log(.app, .error, "Unable to inject provider modules: \(error)")
throw error
}