mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-01-18 22:49:10 +00:00
Add "Refresh infrastructure" in server lists (#938)
Refactoring: - Split Providers and VPN views - Rename VPNProviderServerView subviews - Reuse RefreshInfrastructureButton Closes #929
This commit is contained in:
parent
8b043d8a4f
commit
b357d985ed
@ -27,6 +27,7 @@ import CommonAPI
|
||||
import CommonLibrary
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
import UILibrary
|
||||
|
||||
struct ProviderContentModifier<Entity, ProviderRows>: ViewModifier where Entity: ProviderEntity, Entity.Configuration: ProviderConfigurationIdentifiable & Codable, ProviderRows: View {
|
||||
|
||||
@ -77,18 +78,10 @@ private extension ProviderContentModifier {
|
||||
providerPicker
|
||||
.themeSection()
|
||||
|
||||
if providerId != nil {
|
||||
if let providerId {
|
||||
Group {
|
||||
providerRows
|
||||
refreshButton {
|
||||
HStack {
|
||||
Text(Strings.Views.Providers.refreshInfrastructure)
|
||||
if providerManager.isLoading {
|
||||
Spacer()
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
}
|
||||
RefreshInfrastructureButton(apis: apis, providerId: providerId)
|
||||
}
|
||||
.themeSection(footer: lastUpdatedString)
|
||||
}
|
||||
@ -99,7 +92,7 @@ private extension ProviderContentModifier {
|
||||
Section {
|
||||
providerPicker
|
||||
}
|
||||
if providerId != nil {
|
||||
if let providerId {
|
||||
Section {
|
||||
providerRows
|
||||
HStack {
|
||||
@ -108,9 +101,7 @@ private extension ProviderContentModifier {
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
Spacer()
|
||||
refreshButton {
|
||||
Text(Strings.Views.Providers.refreshInfrastructure)
|
||||
}
|
||||
RefreshInfrastructureButton(apis: apis, providerId: providerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,10 +118,6 @@ private extension ProviderContentModifier {
|
||||
)
|
||||
}
|
||||
|
||||
func refreshButton<Label>(label: () -> Label) -> some View where Label: View {
|
||||
Button(action: onRefreshInfrastructure, label: label)
|
||||
}
|
||||
|
||||
var supportedProviders: [ProviderMetadata] {
|
||||
providerManager
|
||||
.providers
|
||||
@ -185,15 +172,6 @@ private extension ProviderContentModifier {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func onRefreshInfrastructure() {
|
||||
guard let providerId else {
|
||||
return
|
||||
}
|
||||
Task {
|
||||
await refreshInfrastructure(for: providerId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Preview
|
||||
|
@ -29,6 +29,9 @@ import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
struct VPNFiltersView: View {
|
||||
let apis: [APIMapper]
|
||||
|
||||
let providerId: ProviderID
|
||||
|
||||
@ObservedObject
|
||||
var model: Model
|
||||
@ -47,6 +50,7 @@ struct VPNFiltersView: View {
|
||||
HStack {
|
||||
favoritesToggle
|
||||
Spacer()
|
||||
RefreshInfrastructureButton(apis: apis, providerId: providerId)
|
||||
clearFiltersButton
|
||||
}
|
||||
#endif
|
||||
@ -88,7 +92,7 @@ private extension VPNFiltersView {
|
||||
}
|
||||
|
||||
var presetPicker: some View {
|
||||
Picker(Strings.Views.Providers.Vpn.preset, selection: $model.filters.presetId) {
|
||||
Picker(Strings.Views.Vpn.preset, selection: $model.filters.presetId) {
|
||||
Text(Strings.Global.Nouns.any)
|
||||
.tag(nil as String?)
|
||||
ForEach(model.presets, id: \.presetId) {
|
||||
@ -111,6 +115,10 @@ private extension VPNFiltersView {
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
VPNFiltersView(model: .init())
|
||||
VPNFiltersView(
|
||||
apis: [API.bundled],
|
||||
providerId: .mullvad,
|
||||
model: .init()
|
||||
)
|
||||
}
|
||||
}
|
@ -71,6 +71,8 @@ extension VPNProviderServerView {
|
||||
var body: some View {
|
||||
debugChanges()
|
||||
return ContentView(
|
||||
apis: apis,
|
||||
providerId: providerId,
|
||||
servers: filteredServers,
|
||||
selectedServer: selectedServer,
|
||||
isFiltering: isFiltering,
|
@ -84,7 +84,11 @@ extension VPNProviderServerView {
|
||||
}
|
||||
|
||||
var filtersView: some View {
|
||||
VPNFiltersView(model: filtersViewModel)
|
||||
VPNFiltersView(
|
||||
apis: apis,
|
||||
providerId: providerId,
|
||||
model: filtersViewModel
|
||||
)
|
||||
}
|
||||
|
||||
var initialFilters: VPNFilters? {
|
@ -32,6 +32,10 @@ import SwiftUI
|
||||
|
||||
extension VPNProviderServerView {
|
||||
struct ContentView: View {
|
||||
let apis: [APIMapper]
|
||||
|
||||
let providerId: ProviderID
|
||||
|
||||
let servers: [VPNServer]
|
||||
|
||||
let selectedServer: VPNServer?
|
||||
@ -68,6 +72,7 @@ private extension VPNProviderServerView.ContentView {
|
||||
List {
|
||||
Section {
|
||||
Toggle(Strings.Views.Providers.onlyFavorites, isOn: $filtersViewModel.onlyShowsFavorites)
|
||||
RefreshInfrastructureButton(apis: apis, providerId: providerId)
|
||||
}
|
||||
Group {
|
||||
if isFiltering || !servers.isEmpty {
|
||||
@ -82,7 +87,7 @@ private extension VPNProviderServerView.ContentView {
|
||||
}
|
||||
}
|
||||
.themeSection(
|
||||
header: filtersViewModel.filters.categoryName ?? Strings.Views.Providers.Vpn.Category.any
|
||||
header: filtersViewModel.filters.categoryName ?? Strings.Views.Vpn.Category.any
|
||||
)
|
||||
.onLoad {
|
||||
if let selectedServer = selectedServer {
|
||||
@ -93,7 +98,7 @@ private extension VPNProviderServerView.ContentView {
|
||||
}
|
||||
|
||||
var emptyView: some View {
|
||||
Text(Strings.Views.Providers.Vpn.noServers)
|
||||
Text(Strings.Views.Vpn.noServers)
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +165,9 @@ private extension VPNProviderServerView.ContentView {
|
||||
list.append($0)
|
||||
map[code] = list
|
||||
}
|
||||
serversByCountryCode = map
|
||||
withAnimation {
|
||||
serversByCountryCode = map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,10 @@ extension VPNProviderServerView {
|
||||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
||||
let apis: [APIMapper]
|
||||
|
||||
let providerId: ProviderID
|
||||
|
||||
let servers: [VPNServer]
|
||||
|
||||
let selectedServer: VPNServer?
|
@ -26,7 +26,7 @@
|
||||
import SwiftUI
|
||||
|
||||
extension View {
|
||||
public func debugChanges(condition: Bool = true) {
|
||||
public func debugChanges(condition: Bool = false) {
|
||||
if condition {
|
||||
Self._printChanges()
|
||||
}
|
||||
|
@ -830,16 +830,6 @@ public enum Strings {
|
||||
/// Loading...
|
||||
public static let loading = Strings.tr("Localizable", "views.providers.last_updated.loading", fallback: "Loading...")
|
||||
}
|
||||
public enum Vpn {
|
||||
/// No servers
|
||||
public static let noServers = Strings.tr("Localizable", "views.providers.vpn.no_servers", fallback: "No servers")
|
||||
/// Preset
|
||||
public static let preset = Strings.tr("Localizable", "views.providers.vpn.preset", fallback: "Preset")
|
||||
public enum Category {
|
||||
/// All categories
|
||||
public static let any = Strings.tr("Localizable", "views.providers.vpn.category.any", fallback: "All categories")
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum Purchased {
|
||||
/// No purchases
|
||||
@ -883,6 +873,16 @@ public enum Strings {
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum Vpn {
|
||||
/// No servers
|
||||
public static let noServers = Strings.tr("Localizable", "views.vpn.no_servers", fallback: "No servers")
|
||||
/// Preset
|
||||
public static let preset = Strings.tr("Localizable", "views.vpn.preset", fallback: "Preset")
|
||||
public enum Category {
|
||||
/// All categories
|
||||
public static let any = Strings.tr("Localizable", "views.vpn.category.any", fallback: "All categories")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
|
||||
|
@ -115,9 +115,6 @@
|
||||
"views.providers.refresh_infrastructure" = "Refresh infrastructure";
|
||||
"views.providers.last_updated" = "Last updated on %@";
|
||||
"views.providers.last_updated.loading" = "Loading...";
|
||||
"views.providers.vpn.category.any" = "All categories";
|
||||
"views.providers.vpn.preset" = "Preset";
|
||||
"views.providers.vpn.no_servers" = "No servers";
|
||||
|
||||
"views.purchased.title" = "Purchased";
|
||||
"views.purchased.sections.download.header" = "First download";
|
||||
@ -131,6 +128,10 @@
|
||||
"views.ui.purchase_required.purchase.help" = "Purchase required";
|
||||
"views.ui.purchase_required.restricted.help" = "Feature is restricted";
|
||||
|
||||
"views.vpn.category.any" = "All categories";
|
||||
"views.vpn.preset" = "Preset";
|
||||
"views.vpn.no_servers" = "No servers";
|
||||
|
||||
// MARK: Views (Modules)
|
||||
|
||||
"modules.general.sections.storage.header" = "%@";
|
||||
|
@ -0,0 +1,85 @@
|
||||
//
|
||||
// RefreshInfrastructureButton.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 11/25/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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
public struct RefreshInfrastructureButton<Label>: View where Label: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var providerManager: ProviderManager
|
||||
|
||||
private let apis: [APIMapper]
|
||||
|
||||
private let providerId: ProviderID
|
||||
|
||||
private let label: () -> Label
|
||||
|
||||
public init(apis: [APIMapper], providerId: ProviderID, label: @escaping () -> Label) {
|
||||
self.apis = apis
|
||||
self.providerId = providerId
|
||||
self.label = label
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
Button {
|
||||
Task {
|
||||
try await providerManager.fetchVPNInfrastructure(from: apis, for: providerId)
|
||||
}
|
||||
} label: {
|
||||
label()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RefreshInfrastructureButton where Label == RefreshInfrastructureButtonProgressView {
|
||||
public init(apis: [APIMapper], providerId: ProviderID) {
|
||||
self.apis = apis
|
||||
self.providerId = providerId
|
||||
label = {
|
||||
RefreshInfrastructureButtonProgressView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct RefreshInfrastructureButtonProgressView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var providerManager: ProviderManager
|
||||
|
||||
public var body: some View {
|
||||
#if os(iOS)
|
||||
HStack {
|
||||
Text(Strings.Views.Providers.refreshInfrastructure)
|
||||
if providerManager.isLoading {
|
||||
Spacer()
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
#else
|
||||
Text(Strings.Views.Providers.refreshInfrastructure)
|
||||
#endif
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user