mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-02-02 05:52:18 +00:00
Edit OpenVPN remotes (#1052)
In a further subview. Remotes are not editable in providers.
This commit is contained in:
parent
a91f71345c
commit
5caf6da23f
@ -36,6 +36,8 @@ extension OpenVPNView {
|
|||||||
|
|
||||||
let credentialsRoute: (any Hashable)?
|
let credentialsRoute: (any Hashable)?
|
||||||
|
|
||||||
|
let remotesRoute: (any Hashable)?
|
||||||
|
|
||||||
@ObservedObject
|
@ObservedObject
|
||||||
var excludedEndpoints: ObservableList<ExtendedEndpoint>
|
var excludedEndpoints: ObservableList<ExtendedEndpoint>
|
||||||
|
|
||||||
@ -72,6 +74,7 @@ extension OpenVPNView {
|
|||||||
private extension OpenVPNView.ConfigurationView {
|
private extension OpenVPNView.ConfigurationView {
|
||||||
var remotesSection: some View {
|
var remotesSection: some View {
|
||||||
configuration.remotes.map { remotes in
|
configuration.remotes.map { remotes in
|
||||||
|
Group {
|
||||||
ForEach(remotes, id: \.rawValue) { remote in
|
ForEach(remotes, id: \.rawValue) { remote in
|
||||||
SelectableRemoteButton(
|
SelectableRemoteButton(
|
||||||
remote: remote,
|
remote: remote,
|
||||||
@ -79,6 +82,10 @@ private extension OpenVPNView.ConfigurationView {
|
|||||||
excludedEndpoints: excludedEndpoints
|
excludedEndpoints: excludedEndpoints
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if let remotesRoute {
|
||||||
|
NavigationLink(Strings.Global.Actions.edit, value: remotesRoute)
|
||||||
|
}
|
||||||
|
}
|
||||||
.themeSection(header: Strings.Modules.Openvpn.remotes)
|
.themeSection(header: Strings.Modules.Openvpn.remotes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,14 +110,7 @@ private struct SelectableRemoteButton: View {
|
|||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading) {
|
EndpointCardView(endpoint: remote)
|
||||||
Text(remote.address.rawValue)
|
|
||||||
.font(.headline)
|
|
||||||
|
|
||||||
Text("\(remote.proto.socketType.rawValue):\(remote.proto.port.description)")
|
|
||||||
.font(.subheadline)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
|
||||||
Spacer()
|
Spacer()
|
||||||
ThemeImage(.marked)
|
ThemeImage(.marked)
|
||||||
.opaque(!excludedEndpoints.contains(remote))
|
.opaque(!excludedEndpoints.contains(remote))
|
||||||
@ -363,6 +363,7 @@ private extension OpenVPNView.ConfigurationView {
|
|||||||
isServerPushed: false,
|
isServerPushed: false,
|
||||||
configuration: .forPreviews,
|
configuration: .forPreviews,
|
||||||
credentialsRoute: nil,
|
credentialsRoute: nil,
|
||||||
|
remotesRoute: nil,
|
||||||
excludedEndpoints: excludedEndpoints
|
excludedEndpoints: excludedEndpoints
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -94,6 +94,7 @@ private extension OpenVPNView {
|
|||||||
isServerPushed: isServerPushed,
|
isServerPushed: isServerPushed,
|
||||||
configuration: configuration,
|
configuration: configuration,
|
||||||
credentialsRoute: Subroute.credentials,
|
credentialsRoute: Subroute.credentials,
|
||||||
|
remotesRoute: Subroute.editRemotes,
|
||||||
excludedEndpoints: excludedEndpoints
|
excludedEndpoints: excludedEndpoints
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -147,6 +148,8 @@ private extension OpenVPNView {
|
|||||||
case providerConfiguration(OpenVPN.Configuration)
|
case providerConfiguration(OpenVPN.Configuration)
|
||||||
|
|
||||||
case credentials
|
case credentials
|
||||||
|
|
||||||
|
case editRemotes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
@ -169,6 +172,7 @@ private extension OpenVPNView {
|
|||||||
isServerPushed: false,
|
isServerPushed: false,
|
||||||
configuration: configuration.builder(),
|
configuration: configuration.builder(),
|
||||||
credentialsRoute: nil,
|
credentialsRoute: nil,
|
||||||
|
remotesRoute: nil,
|
||||||
excludedEndpoints: excludedEndpoints
|
excludedEndpoints: excludedEndpoints
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -186,6 +190,9 @@ private extension OpenVPNView {
|
|||||||
.navigationTitle(Strings.Modules.Openvpn.credentials)
|
.navigationTitle(Strings.Modules.Openvpn.credentials)
|
||||||
.themeForm()
|
.themeForm()
|
||||||
.themeAnimation(on: draft.wrappedValue.isInteractive, category: .modules)
|
.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)
|
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>) {
|
func onSelectServer(server: VPNServer, preset: VPNPreset<OpenVPN.Configuration>) {
|
||||||
draft.wrappedValue.providerEntity = VPNEntity(server: server, preset: preset)
|
draft.wrappedValue.providerEntity = VPNEntity(server: server, preset: preset)
|
||||||
resetExcludedEndpointsWithCurrentProviderEntity()
|
resetExcludedEndpointsWithCurrentProviderEntity()
|
||||||
|
@ -29,6 +29,10 @@ import PassepartoutKit
|
|||||||
extension Strings {
|
extension Strings {
|
||||||
public enum Unlocalized {
|
public enum Unlocalized {
|
||||||
public enum OpenVPN {
|
public enum OpenVPN {
|
||||||
|
public enum Placeholders {
|
||||||
|
public static let remote = "1.1.1.1:UDP:2222"
|
||||||
|
}
|
||||||
|
|
||||||
public enum XOR: String {
|
public enum XOR: String {
|
||||||
case xormask
|
case xormask
|
||||||
|
|
||||||
|
@ -184,6 +184,8 @@ public enum Strings {
|
|||||||
}
|
}
|
||||||
public enum Global {
|
public enum Global {
|
||||||
public enum Actions {
|
public enum Actions {
|
||||||
|
/// Add
|
||||||
|
public static let add = Strings.tr("Localizable", "global.actions.add", fallback: "Add")
|
||||||
/// Cancel
|
/// Cancel
|
||||||
public static let cancel = Strings.tr("Localizable", "global.actions.cancel", fallback: "Cancel")
|
public static let cancel = Strings.tr("Localizable", "global.actions.cancel", fallback: "Cancel")
|
||||||
/// Connect
|
/// Connect
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Alle Anbieter";
|
"features.providers" = "Alle Anbieter";
|
||||||
"features.routing" = "Benutzerdefiniertes Routing";
|
"features.routing" = "Benutzerdefiniertes Routing";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Hinzufügen";
|
||||||
"global.actions.cancel" = "Abbrechen";
|
"global.actions.cancel" = "Abbrechen";
|
||||||
"global.actions.connect" = "Verbinden";
|
"global.actions.connect" = "Verbinden";
|
||||||
"global.actions.delete" = "Löschen";
|
"global.actions.delete" = "Löschen";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Όλοι οι πάροχοι";
|
"features.providers" = "Όλοι οι πάροχοι";
|
||||||
"features.routing" = "Προσαρμοσμένη Δρομολόγηση";
|
"features.routing" = "Προσαρμοσμένη Δρομολόγηση";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Προσθήκη";
|
||||||
"global.actions.cancel" = "Ακύρωση";
|
"global.actions.cancel" = "Ακύρωση";
|
||||||
"global.actions.connect" = "Σύνδεση";
|
"global.actions.connect" = "Σύνδεση";
|
||||||
"global.actions.delete" = "Διαγραφή";
|
"global.actions.delete" = "Διαγραφή";
|
||||||
|
@ -230,6 +230,7 @@
|
|||||||
|
|
||||||
// MARK: Global (Actions)
|
// MARK: Global (Actions)
|
||||||
|
|
||||||
|
"global.actions.add" = "Add";
|
||||||
"global.actions.cancel" = "Cancel";
|
"global.actions.cancel" = "Cancel";
|
||||||
"global.actions.connect" = "Connect";
|
"global.actions.connect" = "Connect";
|
||||||
"global.actions.delete" = "Delete";
|
"global.actions.delete" = "Delete";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Todos los proveedores";
|
"features.providers" = "Todos los proveedores";
|
||||||
"features.routing" = "Enrutamiento Personalizado";
|
"features.routing" = "Enrutamiento Personalizado";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Agregar";
|
||||||
"global.actions.cancel" = "Cancelar";
|
"global.actions.cancel" = "Cancelar";
|
||||||
"global.actions.connect" = "Conectar";
|
"global.actions.connect" = "Conectar";
|
||||||
"global.actions.delete" = "Eliminar";
|
"global.actions.delete" = "Eliminar";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Tous les fournisseurs";
|
"features.providers" = "Tous les fournisseurs";
|
||||||
"features.routing" = "Routage Personnalisé";
|
"features.routing" = "Routage Personnalisé";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Ajouter";
|
||||||
"global.actions.cancel" = "Annuler";
|
"global.actions.cancel" = "Annuler";
|
||||||
"global.actions.connect" = "Connecter";
|
"global.actions.connect" = "Connecter";
|
||||||
"global.actions.delete" = "Supprimer";
|
"global.actions.delete" = "Supprimer";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Tutti i provider";
|
"features.providers" = "Tutti i provider";
|
||||||
"features.routing" = "Routing Personalizzato";
|
"features.routing" = "Routing Personalizzato";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Aggiungi";
|
||||||
"global.actions.cancel" = "Annulla";
|
"global.actions.cancel" = "Annulla";
|
||||||
"global.actions.connect" = "Connetti";
|
"global.actions.connect" = "Connetti";
|
||||||
"global.actions.delete" = "Elimina";
|
"global.actions.delete" = "Elimina";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Alle providers";
|
"features.providers" = "Alle providers";
|
||||||
"features.routing" = "Aangepaste routering";
|
"features.routing" = "Aangepaste routering";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Toevoegen";
|
||||||
"global.actions.cancel" = "Annuleren";
|
"global.actions.cancel" = "Annuleren";
|
||||||
"global.actions.connect" = "Verbinden";
|
"global.actions.connect" = "Verbinden";
|
||||||
"global.actions.delete" = "Verwijderen";
|
"global.actions.delete" = "Verwijderen";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Wszyscy dostawcy";
|
"features.providers" = "Wszyscy dostawcy";
|
||||||
"features.routing" = "Niestandardowe trasowanie";
|
"features.routing" = "Niestandardowe trasowanie";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Dodaj";
|
||||||
"global.actions.cancel" = "Anuluj";
|
"global.actions.cancel" = "Anuluj";
|
||||||
"global.actions.connect" = "Połącz";
|
"global.actions.connect" = "Połącz";
|
||||||
"global.actions.delete" = "Usuń";
|
"global.actions.delete" = "Usuń";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Todos os provedores";
|
"features.providers" = "Todos os provedores";
|
||||||
"features.routing" = "Roteamento Personalizado";
|
"features.routing" = "Roteamento Personalizado";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Adicionar";
|
||||||
"global.actions.cancel" = "Cancelar";
|
"global.actions.cancel" = "Cancelar";
|
||||||
"global.actions.connect" = "Conectar";
|
"global.actions.connect" = "Conectar";
|
||||||
"global.actions.delete" = "Excluir";
|
"global.actions.delete" = "Excluir";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Все поставщики";
|
"features.providers" = "Все поставщики";
|
||||||
"features.routing" = "Пользовательская маршрутизация";
|
"features.routing" = "Пользовательская маршрутизация";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Добавить";
|
||||||
"global.actions.cancel" = "Отмена";
|
"global.actions.cancel" = "Отмена";
|
||||||
"global.actions.connect" = "Подключить";
|
"global.actions.connect" = "Подключить";
|
||||||
"global.actions.delete" = "Удалить";
|
"global.actions.delete" = "Удалить";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Alla leverantörer";
|
"features.providers" = "Alla leverantörer";
|
||||||
"features.routing" = "Anpassad Ruttning";
|
"features.routing" = "Anpassad Ruttning";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Lägg till";
|
||||||
"global.actions.cancel" = "Avbryt";
|
"global.actions.cancel" = "Avbryt";
|
||||||
"global.actions.connect" = "Anslut";
|
"global.actions.connect" = "Anslut";
|
||||||
"global.actions.delete" = "Ta bort";
|
"global.actions.delete" = "Ta bort";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "Усі постачальники";
|
"features.providers" = "Усі постачальники";
|
||||||
"features.routing" = "Індивідуальна маршрутизація";
|
"features.routing" = "Індивідуальна маршрутизація";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "Додати";
|
||||||
"global.actions.cancel" = "Скасувати";
|
"global.actions.cancel" = "Скасувати";
|
||||||
"global.actions.connect" = "Підключитися";
|
"global.actions.connect" = "Підключитися";
|
||||||
"global.actions.delete" = "Видалити";
|
"global.actions.delete" = "Видалити";
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"features.providers" = "所有提供商";
|
"features.providers" = "所有提供商";
|
||||||
"features.routing" = "自定义路由";
|
"features.routing" = "自定义路由";
|
||||||
"features.sharing" = "%@";
|
"features.sharing" = "%@";
|
||||||
|
"global.actions.add" = "添加";
|
||||||
"global.actions.cancel" = "取消";
|
"global.actions.cancel" = "取消";
|
||||||
"global.actions.connect" = "连接";
|
"global.actions.connect" = "连接";
|
||||||
"global.actions.delete" = "删除";
|
"global.actions.delete" = "删除";
|
||||||
|
46
Library/Sources/UILibrary/Views/UI/EndpointCardView.swift
Normal file
46
Library/Sources/UILibrary/Views/UI/EndpointCardView.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user