parent
9656e5ed29
commit
acf066571a
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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")
|
||||
/// 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.last_updated.loading", fallback: "Loading...")
|
||||
public static let loading = Strings.tr("Localizable", "views.provider.vpn.last_updated.loading", fallback: "Loading...")
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum Settings {
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -27,21 +27,22 @@
|
|||
|
||||
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 {
|
||||
ToolbarItem(placement: .bottomBar) {
|
||||
Button {
|
||||
isFiltersPresented = true
|
||||
} label: {
|
||||
Image(systemName: "line.3.horizontal.decrease")
|
||||
ThemeImage(.filters)
|
||||
}
|
||||
.themeModal(isPresented: $isFiltersPresented) {
|
||||
NavigationStack {
|
||||
VPNFiltersView<Configuration>(manager: manager)
|
||||
.navigationTitle("Filters")
|
||||
.navigationTitle(Strings.Global.filters)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
.presentationDetents([.medium])
|
||||
|
@ -49,5 +50,6 @@ extension VPNFiltersModifier {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
import SwiftUI
|
||||
|
||||
// FIXME: #703, providers UI
|
||||
// FIXME: ###, providers UI, iOS server rows + country flags
|
||||
|
||||
extension VPNProviderServerView {
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -36,6 +36,7 @@ extension Theme {
|
|||
case disclose
|
||||
case editableSectionEdit
|
||||
case editableSectionRemove
|
||||
case filters
|
||||
case footerAdd
|
||||
case hide
|
||||
case info
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue