Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify VPNProviderServerView components #939

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ import PassepartoutKit
import SwiftUI

struct VPNProviderServerView<Configuration>: View where Configuration: ProviderConfigurationIdentifiable & Codable {

@EnvironmentObject
private var providerManager: ProviderManager

var apis: [APIMapper] = API.shared

let moduleId: UUID
Expand All @@ -53,43 +57,79 @@ struct VPNProviderServerView<Configuration>: View where Configuration: ProviderC
.serverId
])

@State
private var servers: [VPNServer] = []

@State
private var isFiltering = false

@State
private var onlyShowsFavorites = false

@StateObject
private var filtersViewModel = VPNFiltersView.Model()

@StateObject
private var favoritesManager = ProviderFavoritesManager()

@StateObject
private var errorHandler: ErrorHandler = .default()

var body: some View {
debugChanges()
return contentView
.themeNavigationDetail()
.withErrorHandler(errorHandler)
return ContainerView(
content: contentView,
filters: filtersView
)
.navigationTitle(title)
.themeNavigationDetail()
.withErrorHandler(errorHandler)
}
}

extension VPNProviderServerView {
var containerView: some View {
ContainerView(
vpnManager: vpnManager,
func contentView() -> some View {
ContentView(
apis: apis,
moduleId: moduleId,
providerId: providerId,
servers: filteredServers,
selectedServer: selectedEntity?.server,
isFiltering: isFiltering,
filtersViewModel: filtersViewModel,
initialFilters: initialFilters,
favoritesManager: favoritesManager,
selectTitle: selectTitle,
onSelect: onSelect,
errorHandler: errorHandler
onSelect: onSelectServer
)
.task {
await loadInitialServers()
}
.onReceive(filtersViewModel.$filters.dropFirst(), perform: onNewFilters)
.onReceive(filtersViewModel.$onlyShowsFavorites, perform: onToggleFavorites)
.onDisappear(perform: onDisappear)
}

var filtersView: some View {
func filtersView() -> some View {
VPNFiltersView(
apis: apis,
providerId: providerId,
model: filtersViewModel
)
}
}

private extension VPNProviderServerView {
var title: String {
providerManager.provider(withId: providerId)?.description ?? Strings.Global.Nouns.servers
}

var filteredServers: [VPNServer] {
if onlyShowsFavorites {
return servers.filter {
favoritesManager.serverIds.contains($0.serverId)
}
}
return servers
}

var initialFilters: VPNFilters? {
guard let selectedEntity, filtersWithSelection else {
Expand All @@ -102,6 +142,71 @@ extension VPNProviderServerView {
#endif
return filters
}

func compatiblePreset(with server: VPNServer) -> VPNPreset<Configuration>? {
vpnManager
.presets
.first {
if let supportedIds = server.provider.supportedPresetIds {
return supportedIds.contains($0.presetId)
}
return true
}
}
}

private extension VPNProviderServerView {
func loadInitialServers() async {
do {
favoritesManager.moduleId = moduleId
let repository = try await providerManager.vpnServerRepository(
from: apis,
for: providerId
)
try await vpnManager.setRepository(repository)
filtersViewModel.load(options: vpnManager.options, initialFilters: initialFilters)
await reloadServers(filters: filtersViewModel.filters)
} catch {
pp_log(.app, .error, "Unable to load VPN repository: \(error)")
errorHandler.handle(error, title: Strings.Global.Nouns.servers)
}
}

func reloadServers(filters: VPNFilters) async {
isFiltering = true
do {
try await Task {
servers = try await vpnManager.filteredServers(with: filters)
filtersViewModel.update(with: servers)
isFiltering = false
}.value
} catch {
pp_log(.app, .error, "Unable to fetch filtered servers: \(error)")
}
}

func onNewFilters(_ filters: VPNFilters) {
Task {
await reloadServers(filters: filters)
}
}

func onToggleFavorites(_ only: Bool) {
onlyShowsFavorites = only
}

func onDisappear() {
favoritesManager.save()
}

func onSelectServer(_ server: VPNServer) {
guard let preset = compatiblePreset(with: server) else {
pp_log(.app, .error, "Unable to find a compatible preset. Supported IDs: \(server.provider.supportedPresetIds ?? [])")
assertionFailure("No compatible presets for server \(server.serverId) (provider=\(vpnManager.providerId), configuration=\(Configuration.providerConfigurationIdentifier), supported=\(server.provider.supportedPresetIds ?? []))")
return
}
onSelect(server, preset)
}
}

// MARK: - Preview
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// VPNProviderServerView+iOS.swift
// VPNProviderServer+Container+macOS.swift
// Passepartout
//
// Created by Davide De Rosa on 11/25/24.
Expand Down Expand Up @@ -28,11 +28,20 @@
import SwiftUI

extension VPNProviderServerView {
var contentView: some View {
containerView
.modifier(FiltersItemModifier {
filtersView
})
struct ContainerView<Content, Filters>: View where Content: View, Filters: View {

@ViewBuilder
let content: Content

@ViewBuilder
let filters: Filters

var body: some View {
content
.modifier(FiltersItemModifier {
filters
})
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// VPNProviderServerContentView+iOS.swift
// VPNProviderServer+Content+iOS.swift
// Passepartout
//
// Created by Davide De Rosa on 10/9/24.
Expand Down
Loading