Edit OpenVPN remotes (#1052)

In a further subview. Remotes are not editable in providers.
This commit is contained in:
Davide 2025-01-05 18:08:29 +01:00 committed by GitHub
parent a91f71345c
commit 5caf6da23f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 153 additions and 14 deletions

View File

@ -36,6 +36,8 @@ extension OpenVPNView {
let credentialsRoute: (any Hashable)?
let remotesRoute: (any Hashable)?
@ObservedObject
var excludedEndpoints: ObservableList<ExtendedEndpoint>
@ -72,12 +74,17 @@ extension OpenVPNView {
private extension OpenVPNView.ConfigurationView {
var remotesSection: some View {
configuration.remotes.map { remotes in
ForEach(remotes, id: \.rawValue) { remote in
SelectableRemoteButton(
remote: remote,
all: Set(remotes),
excludedEndpoints: excludedEndpoints
)
Group {
ForEach(remotes, id: \.rawValue) { remote in
SelectableRemoteButton(
remote: remote,
all: Set(remotes),
excludedEndpoints: excludedEndpoints
)
}
if let remotesRoute {
NavigationLink(Strings.Global.Actions.edit, value: remotesRoute)
}
}
.themeSection(header: Strings.Modules.Openvpn.remotes)
}
@ -103,14 +110,7 @@ private struct SelectableRemoteButton: View {
}
} label: {
HStack {
VStack(alignment: .leading) {
Text(remote.address.rawValue)
.font(.headline)
Text("\(remote.proto.socketType.rawValue):\(remote.proto.port.description)")
.font(.subheadline)
.foregroundStyle(.secondary)
}
EndpointCardView(endpoint: remote)
Spacer()
ThemeImage(.marked)
.opaque(!excludedEndpoints.contains(remote))
@ -363,6 +363,7 @@ private extension OpenVPNView.ConfigurationView {
isServerPushed: false,
configuration: .forPreviews,
credentialsRoute: nil,
remotesRoute: nil,
excludedEndpoints: excludedEndpoints
)
}

View File

@ -0,0 +1,56 @@
//
// OpenVPNView+Remotes.swift
// Passepartout
//
// Created by Davide De Rosa on 1/5/25.
// 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 SwiftUI
extension OpenVPNView {
struct RemotesView: View {
@EnvironmentObject
private var theme: Theme
@Binding
var remotes: [String]
var body: some View {
Form {
theme.listSection(
Strings.Modules.Openvpn.remotes,
addTitle: Strings.Global.Actions.add,
originalItems: $remotes,
itemLabel: {
if $0 {
Text($1.wrappedValue)
} else {
ThemeTextField("", text: $1, placeholder: Strings.Unlocalized.OpenVPN.Placeholders.remote)
}
}
)
}
.labelsHidden()
.themeForm()
}
}
}

View File

@ -94,6 +94,7 @@ private extension OpenVPNView {
isServerPushed: isServerPushed,
configuration: configuration,
credentialsRoute: Subroute.credentials,
remotesRoute: Subroute.editRemotes,
excludedEndpoints: excludedEndpoints
)
} else {
@ -147,6 +148,8 @@ private extension OpenVPNView {
case providerConfiguration(OpenVPN.Configuration)
case credentials
case editRemotes
}
@ViewBuilder
@ -169,6 +172,7 @@ private extension OpenVPNView {
isServerPushed: false,
configuration: configuration.builder(),
credentialsRoute: nil,
remotesRoute: nil,
excludedEndpoints: excludedEndpoints
)
}
@ -186,6 +190,9 @@ private extension OpenVPNView {
.navigationTitle(Strings.Modules.Openvpn.credentials)
.themeForm()
.themeAnimation(on: draft.wrappedValue.isInteractive, category: .modules)
case .editRemotes:
RemotesView(remotes: editableRemotesBinding)
}
}
}
@ -197,6 +204,16 @@ private extension OpenVPNView {
editor.excludedEndpoints(for: module.id, preferences: modulePreferences)
}
var editableRemotesBinding: Binding<[String]> {
Binding {
draft.wrappedValue.configurationBuilder?.remotes?.map(\.rawValue) ?? []
} set: {
draft.wrappedValue.configurationBuilder?.remotes = $0.compactMap {
ExtendedEndpoint(rawValue: $0)
}
}
}
func onSelectServer(server: VPNServer, preset: VPNPreset<OpenVPN.Configuration>) {
draft.wrappedValue.providerEntity = VPNEntity(server: server, preset: preset)
resetExcludedEndpointsWithCurrentProviderEntity()

View File

@ -29,6 +29,10 @@ import PassepartoutKit
extension Strings {
public enum Unlocalized {
public enum OpenVPN {
public enum Placeholders {
public static let remote = "1.1.1.1:UDP:2222"
}
public enum XOR: String {
case xormask

View File

@ -184,6 +184,8 @@ public enum Strings {
}
public enum Global {
public enum Actions {
/// Add
public static let add = Strings.tr("Localizable", "global.actions.add", fallback: "Add")
/// Cancel
public static let cancel = Strings.tr("Localizable", "global.actions.cancel", fallback: "Cancel")
/// Connect

View File

@ -54,6 +54,7 @@
"features.providers" = "Alle Anbieter";
"features.routing" = "Benutzerdefiniertes Routing";
"features.sharing" = "%@";
"global.actions.add" = "Hinzufügen";
"global.actions.cancel" = "Abbrechen";
"global.actions.connect" = "Verbinden";
"global.actions.delete" = "Löschen";

View File

@ -54,6 +54,7 @@
"features.providers" = "Όλοι οι πάροχοι";
"features.routing" = "Προσαρμοσμένη Δρομολόγηση";
"features.sharing" = "%@";
"global.actions.add" = "Προσθήκη";
"global.actions.cancel" = "Ακύρωση";
"global.actions.connect" = "Σύνδεση";
"global.actions.delete" = "Διαγραφή";

View File

@ -230,6 +230,7 @@
// MARK: Global (Actions)
"global.actions.add" = "Add";
"global.actions.cancel" = "Cancel";
"global.actions.connect" = "Connect";
"global.actions.delete" = "Delete";

View File

@ -54,6 +54,7 @@
"features.providers" = "Todos los proveedores";
"features.routing" = "Enrutamiento Personalizado";
"features.sharing" = "%@";
"global.actions.add" = "Agregar";
"global.actions.cancel" = "Cancelar";
"global.actions.connect" = "Conectar";
"global.actions.delete" = "Eliminar";

View File

@ -54,6 +54,7 @@
"features.providers" = "Tous les fournisseurs";
"features.routing" = "Routage Personnalisé";
"features.sharing" = "%@";
"global.actions.add" = "Ajouter";
"global.actions.cancel" = "Annuler";
"global.actions.connect" = "Connecter";
"global.actions.delete" = "Supprimer";

View File

@ -54,6 +54,7 @@
"features.providers" = "Tutti i provider";
"features.routing" = "Routing Personalizzato";
"features.sharing" = "%@";
"global.actions.add" = "Aggiungi";
"global.actions.cancel" = "Annulla";
"global.actions.connect" = "Connetti";
"global.actions.delete" = "Elimina";

View File

@ -54,6 +54,7 @@
"features.providers" = "Alle providers";
"features.routing" = "Aangepaste routering";
"features.sharing" = "%@";
"global.actions.add" = "Toevoegen";
"global.actions.cancel" = "Annuleren";
"global.actions.connect" = "Verbinden";
"global.actions.delete" = "Verwijderen";

View File

@ -54,6 +54,7 @@
"features.providers" = "Wszyscy dostawcy";
"features.routing" = "Niestandardowe trasowanie";
"features.sharing" = "%@";
"global.actions.add" = "Dodaj";
"global.actions.cancel" = "Anuluj";
"global.actions.connect" = "Połącz";
"global.actions.delete" = "Usuń";

View File

@ -54,6 +54,7 @@
"features.providers" = "Todos os provedores";
"features.routing" = "Roteamento Personalizado";
"features.sharing" = "%@";
"global.actions.add" = "Adicionar";
"global.actions.cancel" = "Cancelar";
"global.actions.connect" = "Conectar";
"global.actions.delete" = "Excluir";

View File

@ -54,6 +54,7 @@
"features.providers" = "Все поставщики";
"features.routing" = "Пользовательская маршрутизация";
"features.sharing" = "%@";
"global.actions.add" = "Добавить";
"global.actions.cancel" = "Отмена";
"global.actions.connect" = "Подключить";
"global.actions.delete" = "Удалить";

View File

@ -54,6 +54,7 @@
"features.providers" = "Alla leverantörer";
"features.routing" = "Anpassad Ruttning";
"features.sharing" = "%@";
"global.actions.add" = "Lägg till";
"global.actions.cancel" = "Avbryt";
"global.actions.connect" = "Anslut";
"global.actions.delete" = "Ta bort";

View File

@ -54,6 +54,7 @@
"features.providers" = "Усі постачальники";
"features.routing" = "Індивідуальна маршрутизація";
"features.sharing" = "%@";
"global.actions.add" = "Додати";
"global.actions.cancel" = "Скасувати";
"global.actions.connect" = "Підключитися";
"global.actions.delete" = "Видалити";

View File

@ -54,6 +54,7 @@
"features.providers" = "所有提供商";
"features.routing" = "自定义路由";
"features.sharing" = "%@";
"global.actions.add" = "添加";
"global.actions.cancel" = "取消";
"global.actions.connect" = "连接";
"global.actions.delete" = "删除";

View File

@ -0,0 +1,46 @@
//
// EndpointCardView.swift
// Passepartout
//
// Created by Davide De Rosa on 1/5/25.
// 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 EndpointCardView: View {
private let endpoint: ExtendedEndpoint
public init(endpoint: ExtendedEndpoint) {
self.endpoint = endpoint
}
public var body: some View {
VStack(alignment: .leading) {
Text(endpoint.address.rawValue)
.font(.headline)
Text("\(endpoint.proto.socketType.rawValue):\(endpoint.proto.port.description)")
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
}