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",
|
||||
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
|
||||
"state" : {
|
||||
"revision" : "7b3f68b30d2c3ac434c5aaa10853bd3709c5c308"
|
||||
"revision" : "3934b7a4e64624d499f0d52d9053560554bd4be8"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -28,7 +28,7 @@ let package = Package(
|
|||
],
|
||||
dependencies: [
|
||||
// .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(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"),
|
||||
|
|
|
@ -31,6 +31,8 @@ extension Issue {
|
|||
struct Metadata {
|
||||
let profile: Profile?
|
||||
|
||||
let provider: (ProviderID, Date?)?
|
||||
|
||||
let configuration: PassepartoutConfiguration
|
||||
|
||||
let versionString: String
|
||||
|
@ -73,7 +75,7 @@ extension Issue {
|
|||
purchasedProducts: metadata.purchasedProducts,
|
||||
appLog: appLog,
|
||||
tunnelLog: tunnelLog,
|
||||
providerId: metadata.profile?.firstProviderModuleWithMetadata?.1.id
|
||||
provider: metadata.provider
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,12 +52,14 @@ struct Issue: Identifiable {
|
|||
|
||||
let providerName: String?
|
||||
|
||||
let providerLastUpdate: Date?
|
||||
|
||||
init(
|
||||
appLine: String?,
|
||||
purchasedProducts: Set<AppProduct>,
|
||||
appLog: Data? = nil,
|
||||
tunnelLog: Data? = nil,
|
||||
providerId: ProviderID? = nil
|
||||
provider: (ProviderID, Date?)? = nil
|
||||
) {
|
||||
id = UUID()
|
||||
self.appLine = appLine
|
||||
|
@ -85,7 +87,8 @@ struct Issue: Identifiable {
|
|||
osLine = "\(osName) \(osVersion)"
|
||||
deviceLine = deviceType
|
||||
|
||||
providerName = providerId?.rawValue
|
||||
providerName = provider?.0.rawValue
|
||||
providerLastUpdate = provider?.1
|
||||
}
|
||||
|
||||
var body: String {
|
||||
|
@ -94,7 +97,7 @@ struct Issue: Identifiable {
|
|||
.replacingOccurrences(of: "$osLine", with: osLine)
|
||||
.replacingOccurrences(of: "$deviceLine", with: deviceLine ?? "unknown")
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -452,6 +452,36 @@ public enum Strings {
|
|||
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 Confirmation {
|
||||
/// 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 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 Rows {
|
||||
/// Ask before quit
|
||||
|
|
|
@ -133,15 +133,6 @@
|
|||
"views.profile.rows.add_module" = "Add module";
|
||||
"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.rows.confirm_quit" = "Ask before quit";
|
||||
"views.settings.rows.lock_in_background" = "Lock in background";
|
||||
|
@ -232,6 +223,19 @@
|
|||
"modules.wireguard.preshared_key" = "Pre-shared key";
|
||||
"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
|
||||
|
||||
"ui.connection_status.on_demand_suffix" = " (on-demand)";
|
||||
|
|
|
@ -32,6 +32,9 @@ struct ReportIssueButton {
|
|||
@EnvironmentObject
|
||||
private var profileManager: ProfileManager
|
||||
|
||||
@EnvironmentObject
|
||||
private var providerManager: ProviderManager
|
||||
|
||||
let tunnel: Tunnel
|
||||
|
||||
let title: String
|
||||
|
@ -53,4 +56,12 @@ struct ReportIssueButton {
|
|||
}
|
||||
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(
|
||||
profile: installedProfile,
|
||||
provider: currentProvider,
|
||||
configuration: .shared,
|
||||
versionString: BundleConfiguration.mainVersionString,
|
||||
purchasedProducts: purchasedProducts,
|
||||
|
|
|
@ -51,6 +51,7 @@ private extension ReportIssueButton {
|
|||
}
|
||||
let issue = await Issue.withMetadata(.init(
|
||||
profile: installedProfile,
|
||||
provider: currentProvider,
|
||||
configuration: .shared,
|
||||
versionString: BundleConfiguration.mainVersionString,
|
||||
purchasedProducts: purchasedProducts,
|
||||
|
|
|
@ -132,6 +132,7 @@ private extension OpenVPNView {
|
|||
configurationType: OpenVPN.Configuration.self,
|
||||
selectedEntity: providerEntity.wrappedValue,
|
||||
filtersWithSelection: true,
|
||||
selectTitle: Strings.Providers.selectEntity,
|
||||
onSelect: onSelectServer
|
||||
)
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ private extension ProviderContentModifier {
|
|||
providerRows
|
||||
refreshButton {
|
||||
HStack {
|
||||
Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
|
||||
Text(Strings.Providers.refreshInfrastructure)
|
||||
if providerManager.isLoading {
|
||||
Spacer()
|
||||
ProgressView()
|
||||
|
@ -102,7 +102,7 @@ private extension ProviderContentModifier {
|
|||
}
|
||||
Spacer()
|
||||
refreshButton {
|
||||
Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
|
||||
Text(Strings.Providers.refreshInfrastructure)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,9 +138,9 @@ private extension ProviderContentModifier {
|
|||
|
||||
var lastUpdatedString: String? {
|
||||
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 {
|
||||
Picker(Strings.Global.provider, selection: $providerId) {
|
||||
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?)
|
||||
ForEach(providers, id: \.id) {
|
||||
Text($0.description)
|
||||
|
|
|
@ -134,7 +134,7 @@ private extension VPNFiltersView.Subview {
|
|||
}
|
||||
|
||||
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)
|
||||
.tag(nil as String?)
|
||||
ForEach(presets, id: \.presetId) {
|
||||
|
@ -145,7 +145,7 @@ private extension VPNFiltersView.Subview {
|
|||
}
|
||||
|
||||
var clearFiltersButton: some View {
|
||||
Button(Strings.Views.Provider.clearFilters, role: .destructive) {
|
||||
Button(Strings.Providers.clearFilters, role: .destructive) {
|
||||
filters = VPNFilters()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ struct VPNProviderServerCoordinator<Configuration>: View where Configuration: Pr
|
|||
configurationType: Configuration.self,
|
||||
selectedEntity: selectedEntity,
|
||||
filtersWithSelection: false,
|
||||
selectTitle: Strings.Global.connect,
|
||||
onSelect: onSelect
|
||||
)
|
||||
.toolbar {
|
||||
|
|
|
@ -43,6 +43,8 @@ struct VPNProviderServerView<Configuration>: View where Configuration: ProviderC
|
|||
|
||||
let filtersWithSelection: Bool
|
||||
|
||||
let selectTitle: String
|
||||
|
||||
let onSelect: (_ server: VPNServer, _ preset: VPNPreset<Configuration>) -> Void
|
||||
|
||||
@StateObject
|
||||
|
@ -64,10 +66,12 @@ struct VPNProviderServerView<Configuration>: View where Configuration: ProviderC
|
|||
manager: manager,
|
||||
selectedServer: selectedEntity?.server,
|
||||
filters: $filters,
|
||||
selectTitle: selectTitle,
|
||||
onSelect: selectServer
|
||||
)
|
||||
.withErrorHandler(errorHandler)
|
||||
.navigationTitle(Strings.Global.servers)
|
||||
.themeNavigationDetail()
|
||||
.onLoad {
|
||||
Task {
|
||||
do {
|
||||
|
@ -124,6 +128,7 @@ extension VPNProviderServerView {
|
|||
configurationType: OpenVPN.Configuration.self,
|
||||
selectedEntity: nil,
|
||||
filtersWithSelection: false,
|
||||
selectTitle: "Select",
|
||||
onSelect: { _, _ in }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@ extension VPNProviderServerView {
|
|||
@Binding
|
||||
var filters: VPNFilters
|
||||
|
||||
// unused
|
||||
let selectTitle: String
|
||||
|
||||
let onSelect: (VPNServer) -> Void
|
||||
|
||||
@State
|
||||
|
@ -56,11 +59,20 @@ extension VPNProviderServerView {
|
|||
|
||||
private extension VPNProviderServerView.Subview {
|
||||
var listView: some View {
|
||||
List {
|
||||
ZStack {
|
||||
if manager.isFiltering {
|
||||
ProgressView()
|
||||
} else if !manager.filteredServers.isEmpty {
|
||||
List {
|
||||
Section {
|
||||
ForEach(countryCodes, id: \.self, content: countryView)
|
||||
} header: {
|
||||
Text(filters.categoryName ?? Strings.Providers.Vpn.Category.any)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ForEach(countryCodes, id: \.self, content: countryView)
|
||||
Text(Strings.Providers.Vpn.noServers)
|
||||
.themeEmptyMessage()
|
||||
}
|
||||
}
|
||||
.themeAnimation(on: manager.isFiltering, category: .providers)
|
||||
|
@ -144,6 +156,7 @@ private extension VPNProviderServerView.Subview {
|
|||
configurationType: OpenVPN.Configuration.self,
|
||||
selectedEntity: nil,
|
||||
filtersWithSelection: false,
|
||||
selectTitle: "Select",
|
||||
onSelect: { _, _ in }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ extension VPNProviderServerView {
|
|||
@Binding
|
||||
var filters: VPNFilters
|
||||
|
||||
let selectTitle: String
|
||||
|
||||
let onSelect: (VPNServer) -> Void
|
||||
|
||||
var body: some View {
|
||||
|
@ -72,7 +74,7 @@ private extension VPNProviderServerView.Subview {
|
|||
Button {
|
||||
onSelect(server)
|
||||
} label: {
|
||||
Text(Strings.Views.Provider.selectServer)
|
||||
Text(selectTitle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +100,7 @@ private extension VPNProviderServerView.Subview {
|
|||
configurationType: OpenVPN.Configuration.self,
|
||||
selectedEntity: nil,
|
||||
filtersWithSelection: false,
|
||||
selectTitle: "Select",
|
||||
onSelect: { _, _ in }
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue