diff --git a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index bb4f898d..6f43a034 100644
--- a/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Passepartout.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -41,8 +41,7 @@
"kind" : "remoteSourceControl",
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
"state" : {
- "revision" : "ebb142e836e9e2a8a1867c7ae3d4f44b6b96e917",
- "version" : "0.9.1"
+ "revision" : "aeb982951e2798863e28f55081dd25e2221083e3"
}
},
{
diff --git a/Passepartout/Library/Package.swift b/Passepartout/Library/Package.swift
index 7a239ef0..a4ddb0e4 100644
--- a/Passepartout/Library/Package.swift
+++ b/Passepartout/Library/Package.swift
@@ -27,8 +27,8 @@ let package = Package(
)
],
dependencies: [
- .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.1"),
-// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "0bfd4578b71a905584cdd5c9c39ab3087521af78"),
+// .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", from: "0.9.0"),
+ .package(url: "git@github.com:passepartoutvpn/passepartoutkit-source", revision: "aeb982951e2798863e28f55081dd25e2221083e3"),
// .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"),
diff --git a/Passepartout/Library/Sources/AppLibrary/Business/ProviderFactory.swift b/Passepartout/Library/Sources/AppLibrary/Business/ProviderFactory.swift
deleted file mode 100644
index 9f143e53..00000000
--- a/Passepartout/Library/Sources/AppLibrary/Business/ProviderFactory.swift
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// ProviderFactory.swift
-// Passepartout
-//
-// Created by Davide De Rosa on 10/8/24.
-// Copyright (c) 2024 Davide De Rosa. All rights reserved.
-//
-// https://github.com/passepartoutvpn
-//
-// This file is part of Passepartout.
-//
-// Passepartout is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Passepartout is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Passepartout. If not, see .
-//
-
-import Foundation
-import PassepartoutKit
-
-@MainActor
-public final class ProviderFactory: ObservableObject {
- public let providerManager: ProviderManager
-
- public let vpnProviderManager: VPNProviderManager
-
- public init(providerManager: ProviderManager, vpnProviderManager: VPNProviderManager) {
- self.providerManager = providerManager
- self.vpnProviderManager = vpnProviderManager
- }
-}
diff --git a/Passepartout/Library/Sources/AppUI/Business/AppContext.swift b/Passepartout/Library/Sources/AppUI/Business/AppContext.swift
index 280bb1b1..5a88428f 100644
--- a/Passepartout/Library/Sources/AppUI/Business/AppContext.swift
+++ b/Passepartout/Library/Sources/AppUI/Business/AppContext.swift
@@ -46,7 +46,7 @@ public final class AppContext: ObservableObject {
public let registry: Registry
- public let providerFactory: ProviderFactory
+ public let providerManager: ProviderManager
private let constants: Constants
@@ -59,7 +59,7 @@ public final class AppContext: ObservableObject {
tunnel: Tunnel,
tunnelEnvironment: TunnelEnvironment,
registry: Registry,
- providerFactory: ProviderFactory,
+ providerManager: ProviderManager,
constants: Constants
) {
self.iapManager = iapManager
@@ -73,7 +73,7 @@ public final class AppContext: ObservableObject {
interval: constants.tunnel.refreshInterval
)
self.registry = registry
- self.providerFactory = providerFactory
+ self.providerManager = providerManager
self.constants = constants
subscriptions = []
diff --git a/Passepartout/Library/Sources/AppUI/L10n/PassepartoutKit+L10n.swift b/Passepartout/Library/Sources/AppUI/L10n/PassepartoutKit+L10n.swift
index e88c7d34..a076507b 100644
--- a/Passepartout/Library/Sources/AppUI/L10n/PassepartoutKit+L10n.swift
+++ b/Passepartout/Library/Sources/AppUI/L10n/PassepartoutKit+L10n.swift
@@ -145,13 +145,13 @@ extension OnDemandModule.Policy: LocalizableEntity {
}
extension VPNServer {
- public var sortableRegion: String {
- [countryCodes.first?.localizedAsRegionCode, area]
+ public var region: String {
+ [provider.countryCodes.first?.localizedAsRegionCode, provider.area]
.compactMap { $0 }
.joined(separator: " - ")
}
- public var sortableAddresses: String {
+ public var address: String {
if let hostname {
return hostname
}
diff --git a/Passepartout/Library/Sources/AppUI/Mock/Mock.swift b/Passepartout/Library/Sources/AppUI/Mock/Mock.swift
index d913c1ca..1315a310 100644
--- a/Passepartout/Library/Sources/AppUI/Mock/Mock.swift
+++ b/Passepartout/Library/Sources/AppUI/Mock/Mock.swift
@@ -62,9 +62,9 @@ extension AppContext {
tunnel: Tunnel(strategy: FakeTunnelStrategy(environment: env)),
tunnelEnvironment: env,
registry: registry,
- providerFactory: ProviderFactory(
- providerManager: ProviderManager(repository: InMemoryProviderRepository()),
- vpnProviderManager: VPNProviderManager(repository: InMemoryVPNProviderRepository())
+ providerManager: ProviderManager(
+ repository: InMemoryProviderRepository(),
+ vpnRepository: InMemoryVPNProviderRepository()
),
constants: .shared
)
@@ -83,12 +83,6 @@ extension ProfileManager {
}
}
-extension ProviderFactory {
- public static var mock: ProviderFactory {
- AppContext.mock.providerFactory
- }
-}
-
extension ProfileProcessor {
public static var mock: ProfileProcessor {
AppContext.mock.profileProcessor
@@ -107,6 +101,12 @@ extension ConnectionObserver {
}
}
+extension ProviderManager {
+ public static var mock: ProviderManager {
+ AppContext.mock.providerManager
+ }
+}
+
// MARK: - Profile
extension Profile {
diff --git a/Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView.swift b/Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView.swift
index 45558b80..47314499 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Modules/OpenVPNView.swift
@@ -60,6 +60,9 @@ struct OpenVPNView: View {
@Binding
private var providerEntity: VPNEntity?
+ @StateObject
+ private var vpnProviderManager = VPNProviderManager()
+
init(serverConfiguration: OpenVPN.Configuration) {
let module = OpenVPNModule.Builder(configurationBuilder: serverConfiguration.builder())
let editor = ProfileEditor(modules: [module])
@@ -99,41 +102,35 @@ private extension OpenVPNView {
var providerModifier: some ViewModifier {
ProviderPanelModifier(
isRequired: draft.configurationBuilder == nil,
+ entityType: VPNEntity.self,
providerId: $providerId,
- selectedEntity: $providerEntity,
- providerContent: providerContentView
+ providerContent: providerContentView,
+ onSelectProvider: onSelectProvider
)
}
@ViewBuilder
- var contentView: some View {
- credentialsView
- if providerId == nil {
- manualView
- }
+ func providerContentView(providerId: ProviderID) -> some View {
+ providerServerRow
+ moduleGroup(for: accountRows)
}
- @ViewBuilder
- func providerContentView(providerId: ProviderID, entity: VPNEntity?) -> some View {
- NavigationLink(value: Subroute.providerServer(id: providerId)) {
+ var providerServerRow: some View {
+ NavigationLink(value: Subroute.providerServer) {
HStack {
- Text("Server")
- if let entity {
+ Text(Strings.Global.server)
+ if let providerEntity {
Spacer()
- Text(entity.server.hostname ?? entity.server.serverId)
+ Text(providerEntity.server.hostname ?? providerEntity.server.serverId)
.foregroundStyle(.secondary)
}
}
}
- credentialsView
- }
-
- var credentialsView: some View {
- moduleSection(for: accountRows, header: Strings.Global.account)
}
@ViewBuilder
- var manualView: some View {
+ var contentView: some View {
+ moduleSection(for: accountRows, header: Strings.Global.account)
moduleSection(for: remotesRows, header: Strings.Modules.Openvpn.remotes)
if !isServerPushed {
moduleSection(for: pullRows, header: Strings.Modules.Openvpn.pull)
@@ -159,6 +156,20 @@ private extension OpenVPNView {
}
private extension OpenVPNView {
+ func onSelectProvider(manager: ProviderManager) {
+ guard let providerId else {
+ return
+ }
+ vpnProviderManager.view = manager.vpnView(
+ withId: providerId,
+ initialParameters: .init(sorting: [
+ .localizedCountry,
+ .area,
+ .hostname
+ ])
+ )
+ }
+
func onSelect(server: VPNServer, preset: VPNPreset) {
providerEntity = VPNEntity(server: server, preset: preset)
}
@@ -172,7 +183,7 @@ private extension OpenVPNView {
private extension OpenVPNView {
enum Subroute: Hashable {
- case providerServer(id: ProviderID)
+ case providerServer
case credentials
}
@@ -180,9 +191,9 @@ private extension OpenVPNView {
@ViewBuilder
func destination(for route: Subroute) -> some View {
switch route {
- case .providerServer(let id):
+ case .providerServer:
VPNProviderServerView(
- providerId: id,
+ manager: vpnProviderManager,
onSelect: onSelect
)
diff --git a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleSection.swift b/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleSection.swift
index 19608aaa..8c8a4ed2 100644
--- a/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleSection.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/ProfileEditor/ModuleSection.swift
@@ -71,12 +71,18 @@ struct HashableRoute: Hashable {
}
extension View {
- func moduleSection(for rows: [ModuleRow]?, header: String) -> some View {
+ func moduleGroup(for rows: [ModuleRow]?) -> some View {
rows.map { rows in
Group {
ForEach(rows, id: \.self, content: moduleRowView)
}
- .themeSection(header: header)
+ }
+ }
+
+ func moduleSection(for rows: [ModuleRow]?, header: String) -> some View {
+ rows.map { rows in
+ moduleGroup(for: rows)
+ .themeSection(header: header)
}
}
}
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/ProviderPanelModifier.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/ProviderPanelModifier.swift
index e012ec31..f5b28695 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/ProviderPanelModifier.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/ProviderPanelModifier.swift
@@ -26,6 +26,9 @@
import AppLibrary
import PassepartoutKit
import SwiftUI
+import UtilsLibrary
+
+// FIXME: #703, providers UI, reorg subviews
struct ProviderPanelModifier: ViewModifier where Entity: ProviderEntity, Entity.Configuration: ProviderConfigurationIdentifiable & Codable, ProviderContent: View {
@@ -36,25 +39,31 @@ struct ProviderPanelModifier: ViewModifier where Entity
let isRequired: Bool
+ let entityType: Entity.Type
+
@Binding
var providerId: ProviderID?
- @Binding
- var selectedEntity: Entity?
-
@ViewBuilder
- let providerContent: (ProviderID, Entity?) -> ProviderContent
+ let providerContent: (ProviderID) -> ProviderContent
+
+ let onSelectProvider: (ProviderManager) -> Void
func body(content: Content) -> some View {
- providerPicker
- .task {
- await refreshIndex()
- }
+ debugChanges()
+ return Group {
+ providerPicker
+ .onLoad(perform: loadCurrentProvider)
- if let providerId {
- providerContent(providerId, selectedEntity)
- } else if !isRequired {
- content
+ if let providerId {
+ providerContent(providerId)
+ .asSectionWithTrailingContent {
+ refreshButton
+ }
+ .disabled(providerManager.isLoading)
+ } else if !isRequired {
+ content
+ }
}
}
}
@@ -70,33 +79,91 @@ private extension ProviderPanelModifier {
let hasProviders = !supportedProviders.isEmpty
return Picker(Strings.Global.provider, selection: $providerId) {
if hasProviders {
- Text(Strings.Global.none)
+ // FIXME: #703, providers UI
+ Text("Select a provider")
.tag(nil as ProviderID?)
ForEach(supportedProviders, id: \.id) {
Text($0.description)
.tag($0.id as ProviderID?)
}
} else {
- Text(" ") // enforce constant picker height on iOS
+ // enforce constant picker height on iOS
+ Text(providerManager.isLoading ? "..." : "Unavailable")
.tag(providerId) // tag always exists
}
}
- .onChange(of: providerId) { _ in
- selectedEntity = nil
+ .onChange(of: providerId) { newId in
+ Task {
+ if let newId {
+ await refreshInfrastructure(for: newId)
+ }
+ onSelectProvider(providerManager)
+ }
}
.disabled(!hasProviders)
}
+
+ var refreshButton: some View {
+ Button {
+ guard let providerId else {
+ return
+ }
+ Task {
+ await refreshInfrastructure(for: providerId)
+ }
+ } label: {
+ HStack {
+ Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
+#if os(iOS)
+ if let providerId, providerManager.pendingServices.contains(.provider(providerId)) {
+ Spacer()
+ ProgressView()
+ }
+#endif
+ }
+ }
+ .disabled(providerManager.isLoading || providerId == nil)
+ }
}
private extension ProviderPanelModifier {
+ func loadCurrentProvider() {
+ Task {
+ if let providerId {
+ async let index = await refreshIndex()
+ async let provider = await refreshInfrastructure(for: providerId)
+ _ = await (index, provider)
+ onSelectProvider(providerManager)
+ } else {
+ await refreshIndex()
+ }
+ }
+ }
- // FIXME: #707, fetch bundled providers on launch
- // FIXME: #704, rate-limit fetch
- func refreshIndex() async {
+ @discardableResult
+ func refreshIndex() async -> Bool {
do {
try await providerManager.fetchIndex(from: apis)
+ return true
} catch {
pp_log(.app, .error, "Unable to fetch index: \(error)")
+ return false
+ }
+ }
+
+ @discardableResult
+ func refreshInfrastructure(for providerId: ProviderID) async -> Bool {
+ do {
+ try await providerManager.fetchVPNInfrastructure(
+ from: apis,
+ for: providerId,
+ ofType: Entity.Configuration.self
+ )
+ return true
+ } catch {
+ // FIXME: #703, alert unable to refresh infrastructure
+ pp_log(.app, .error, "Unable to refresh infrastructure: \(error)")
+ return false
}
}
}
@@ -110,26 +177,17 @@ private extension ProviderID {
// MARK: - Preview
#Preview {
- @State
- var providerId: ProviderID? = .hideme
-
- @State
- var vpnEntity: VPNEntity?
-
- return List {
+ List {
EmptyView()
.modifier(ProviderPanelModifier(
apis: [API.bundled],
isRequired: false,
- providerId: $providerId,
- selectedEntity: $vpnEntity,
- providerContent: { _, entity in
- HStack {
- Text("Server")
- Spacer()
- Text(entity?.server.serverId ?? "None")
- }
- }
+ entityType: VPNEntity.self,
+ providerId: .constant(.hideme),
+ providerContent: { _ in
+ Text("Server")
+ },
+ onSelectProvider: { _ in }
))
}
.withMockEnvironment()
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersModifier.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersModifier.swift
index 16a2daed..16b4c00c 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersModifier.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersModifier.swift
@@ -31,16 +31,12 @@ struct VPNFiltersModifier: ViewModifier where Configuration: Deco
@ObservedObject
var manager: VPNProviderManager
- let providerId: ProviderID
-
- let onRefresh: () async -> Void
-
@State
var isFiltersPresented = false
func body(content: Content) -> some View {
contentView(with: content)
- .onChange(of: manager.filters) { _ in
+ .onChange(of: manager.parameters.filters) { _ in
Task {
await manager.applyFilters()
}
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView.swift
index 5d9ded5c..369cbae6 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/VPNFiltersView.swift
@@ -34,10 +34,6 @@ struct VPNFiltersView: View where Configuration: Decodable {
@ObservedObject
var manager: VPNProviderManager
- let providerId: ProviderID
-
- let onRefresh: () async -> Void
-
@State
private var isRefreshing = false
@@ -54,22 +50,16 @@ struct VPNFiltersView: View where Configuration: Decodable {
HStack {
Spacer()
clearFiltersButton
- refreshButton
}
#endif
}
-#if os(iOS)
- Section {
- refreshButton
- }
-#endif
}
}
}
private extension VPNFiltersView {
var categoryPicker: some View {
- Picker("Category", selection: $manager.filters.categoryName) {
+ Picker("Category", selection: $manager.parameters.filters.categoryName) {
Text("Any")
.tag(nil as String?)
ForEach(categories, id: \.self) {
@@ -80,7 +70,7 @@ private extension VPNFiltersView {
}
var countryPicker: some View {
- Picker("Country", selection: $manager.filters.countryCode) {
+ Picker("Country", selection: $manager.parameters.filters.countryCode) {
Text("Any")
.tag(nil as String?)
ForEach(countries, id: \.code) {
@@ -92,8 +82,8 @@ private extension VPNFiltersView {
@ViewBuilder
var presetPicker: some View {
- if manager.anyPresets.count > 1 {
- Picker("Preset", selection: $manager.filters.presetId) {
+ if manager.allPresets.count > 1 {
+ Picker("Preset", selection: $manager.parameters.filters.presetId) {
Text("Any")
.tag(nil as String?)
ForEach(presets, id: \.presetId) {
@@ -111,27 +101,6 @@ private extension VPNFiltersView {
}
}
}
-
- var refreshButton: some View {
- Button {
- Task {
- isRefreshing = true
- await onRefresh()
- isRefreshing = false
- }
- } label: {
- HStack {
- Text(Strings.Views.Provider.Vpn.refreshInfrastructure)
-#if os(iOS)
- if isRefreshing {
- Spacer()
- ProgressView()
- }
-#endif
- }
- }
- .disabled(isRefreshing)
- }
}
private extension VPNFiltersView {
@@ -149,7 +118,7 @@ private extension VPNFiltersView {
let allCodes = manager
.allServers
.values
- .flatMap(\.countryCodes)
+ .flatMap(\.provider.countryCodes)
return Set(allCodes)
.map {
@@ -171,10 +140,6 @@ private extension VPNFiltersView {
#Preview {
NavigationStack {
- VPNFiltersView(
- manager: ProviderFactory.mock.vpnProviderManager,
- providerId: .hideme,
- onRefresh: {}
- )
+ VPNFiltersView(manager: VPNProviderManager())
}
}
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerView.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerView.swift
index 6ef9b0aa..1a4e7f1b 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerView.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/VPNProviderServerView.swift
@@ -29,66 +29,24 @@ import SwiftUI
struct VPNProviderServerView: View where Configuration: ProviderConfigurationIdentifiable & Hashable & Codable {
- @EnvironmentObject
- private var providerManager: ProviderManager
-
- @EnvironmentObject
- private var vpnProviderManager: VPNProviderManager
-
@Environment(\.dismiss)
private var dismiss
- var apis: [APIMapper] = API.shared
-
- let providerId: ProviderID
+ @ObservedObject
+ var manager: VPNProviderManager
let onSelect: (_ server: VPNServer, _ preset: VPNPreset) -> Void
- @State
- private var isLoading = true
-
- @State
- var sortOrder = [
- KeyPathComparator(\VPNServer.sortableRegion)
- ]
-
- @State
- var sortedServers: [VPNServer] = []
-
- // FIXME: #703, flickers on appear
var body: some View {
serversView
- .modifier(VPNFiltersModifier(
- manager: vpnProviderManager,
- providerId: providerId,
- onRefresh: {
- await refreshInfrastructure(for: providerId)
- }
- ))
- .themeAnimation(on: isLoading, category: .providers)
- .navigationTitle(providerMetadata?.description ?? Strings.Global.servers)
- .task {
- await loadInfrastructure(for: providerId)
- }
- .onReceive(vpnProviderManager.$filteredServers, perform: onFilteredServers)
- }
-}
-
-private extension VPNProviderServerView {
- var providerMetadata: ProviderMetadata? {
- providerManager.metadata(withId: providerId)
+ .modifier(VPNFiltersModifier(manager: manager))
+ .navigationTitle(Strings.Global.servers)
}
}
// MARK: - Actions
extension VPNProviderServerView {
- func onFilteredServers(_ servers: [String: VPNServer]) {
- sortedServers = servers
- .values
- .sorted(using: sortOrder)
- }
-
func selectServer(_ server: VPNServer) {
guard let preset = compatiblePreset(with: server) else {
// FIXME: #703, alert select a preset
@@ -101,7 +59,7 @@ extension VPNProviderServerView {
private extension VPNProviderServerView {
func compatiblePreset(with server: VPNServer) -> VPNPreset? {
- vpnProviderManager
+ manager
.presets(ofType: Configuration.self)
.first {
if let supportedIds = server.provider.supportedPresetIds {
@@ -110,38 +68,13 @@ private extension VPNProviderServerView {
return true
}
}
-
- func loadInfrastructure(for providerId: ProviderID) async {
- await vpnProviderManager.setProvider(providerId)
- if await vpnProviderManager.lastUpdated() == nil {
- await refreshInfrastructure(for: providerId)
- }
- isLoading = false
- }
-
- // FIXME: #704, rate-limit fetch
- func refreshInfrastructure(for providerId: ProviderID) async {
- do {
- isLoading = true
- try await vpnProviderManager.fetchInfrastructure(
- from: apis,
- for: providerId,
- ofType: Configuration.self
- )
- isLoading = false
- } catch {
- // FIXME: #703, alert unable to refresh infrastructure
- pp_log(.app, .error, "Unable to refresh infrastructure: \(error)")
- isLoading = false
- }
- }
}
// MARK: - Preview
#Preview {
NavigationStack {
- VPNProviderServerView(apis: [API.bundled], providerId: .protonvpn) { _, _ in
+ VPNProviderServerView(manager: VPNProviderManager()) { _, _ in
}
}
.withMockEnvironment()
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNFiltersModifier+iOS.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNFiltersModifier+iOS.swift
index 9835b676..1fb90575 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNFiltersModifier+iOS.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNFiltersModifier+iOS.swift
@@ -43,7 +43,7 @@ extension VPNFiltersModifier {
}
.themeModal(isPresented: $isFiltersPresented) {
NavigationStack {
- VPNFiltersView(manager: manager, providerId: providerId, onRefresh: onRefresh)
+ VPNFiltersView(manager: manager)
.navigationTitle("Filters")
.navigationBarTitleDisplayMode(.inline)
}
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNProviderServerView+iOS.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNProviderServerView+iOS.swift
index b4f8f7bf..3bbfcfe1 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNProviderServerView+iOS.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/iOS/VPNProviderServerView+iOS.swift
@@ -30,12 +30,12 @@ import SwiftUI
// FIXME: #703, providers UI
extension VPNProviderServerView {
+
+ @ViewBuilder
var serversView: some View {
- sortedServers.nilIfEmpty.map { servers in
- ForEach(sortedServers) { server in
- Button("\(server.hostname ?? server.id) \(server.countryCodes)") {
- selectServer(server)
- }
+ ForEach(manager.filteredServers) { server in
+ Button("\(server.hostname ?? server.id) \(server.provider.countryCodes)") {
+ selectServer(server)
}
}
}
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNFiltersModifier+macOS.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNFiltersModifier+macOS.swift
index 8ac9e1db..1646cebb 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNFiltersModifier+macOS.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNFiltersModifier+macOS.swift
@@ -30,7 +30,7 @@ import SwiftUI
extension VPNFiltersModifier {
func contentView(with content: Content) -> some View {
VStack {
- VPNFiltersView(manager: manager, providerId: providerId, onRefresh: onRefresh)
+ VPNFiltersView(manager: manager)
.padding()
content
}
diff --git a/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNProviderServerView+macOS.swift b/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNProviderServerView+macOS.swift
index 29116da1..2eafd2e4 100644
--- a/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNProviderServerView+macOS.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/Provider/macOS/VPNProviderServerView+macOS.swift
@@ -30,14 +30,18 @@ import SwiftUI
// FIXME: #703, providers UI
extension VPNProviderServerView {
+
+ @ViewBuilder
var serversView: some View {
- Table(sortedServers, sortOrder: $sortOrder) {
- TableColumn("Region", value: \.sortableRegion)
- .width(max: 200.0)
+ Table(manager.filteredServers) {
+ TableColumn("Region") { server in
+ Text(server.region)
+ }
+ .width(max: 200.0)
- TableColumn("Address", value: \.sortableAddresses)
+ TableColumn("Address", value: \.address)
- TableColumn("", value: \.serverId) { server in
+ TableColumn("") { server in
Button {
selectServer(server)
} label: {
diff --git a/Passepartout/Library/Sources/AppUI/Views/UI/View+Environment.swift b/Passepartout/Library/Sources/AppUI/Views/UI/View+Environment.swift
index 55caf1ad..a9642c42 100644
--- a/Passepartout/Library/Sources/AppUI/Views/UI/View+Environment.swift
+++ b/Passepartout/Library/Sources/AppUI/Views/UI/View+Environment.swift
@@ -31,10 +31,9 @@ extension View {
public func withEnvironment(from context: AppContext, theme: Theme) -> some View {
environmentObject(theme)
.environmentObject(context.iapManager)
- .environmentObject(context.connectionObserver)
- .environmentObject(context.providerFactory.providerManager)
- .environmentObject(context.providerFactory.vpnProviderManager)
.environmentObject(context.profileProcessor)
+ .environmentObject(context.connectionObserver)
+ .environmentObject(context.providerManager)
}
public func withMockEnvironment() -> some View {
diff --git a/Passepartout/Library/Sources/CommonLibrary/Shared.swift b/Passepartout/Library/Sources/CommonLibrary/Shared.swift
index 82518295..43c7b44b 100644
--- a/Passepartout/Library/Sources/CommonLibrary/Shared.swift
+++ b/Passepartout/Library/Sources/CommonLibrary/Shared.swift
@@ -63,7 +63,7 @@ extension API {
#if DEBUG
[API.bundled]
#else
- [API.remoteThenBundled]
+ API.remoteThenBundled
#endif
}
diff --git a/Passepartout/Shared/Shared+App.swift b/Passepartout/Shared/Shared+App.swift
index b6747de0..9d6474fb 100644
--- a/Passepartout/Shared/Shared+App.swift
+++ b/Passepartout/Shared/Shared+App.swift
@@ -40,7 +40,7 @@ extension AppContext {
tunnel: .shared,
tunnelEnvironment: .shared,
registry: .shared,
- providerFactory: .shared,
+ providerManager: .shared,
constants: .shared
)
}
@@ -208,10 +208,10 @@ private extension ProfileManager {
// MARK: -
// FIXME: #705, store providers to Core Data
-extension ProviderFactory {
- static let shared = ProviderFactory(
- providerManager: ProviderManager(repository: InMemoryProviderRepository()),
- vpnProviderManager: VPNProviderManager(repository: InMemoryVPNProviderRepository())
+extension ProviderManager {
+ static let shared = ProviderManager(
+ repository: InMemoryProviderRepository(),
+ vpnRepository: InMemoryVPNProviderRepository()
)
}