Refactor a few things about provider flows (#748)
- Move disclosable menu from installed profile view to ThemeDisclosableMenu - Drop unnecessary configurationType modifier parameter - Reorg view-related module extensions to separate files - Reuse .flow fields instead of single blocks - Show specific error on missing provider server selection
This commit is contained in:
parent
39bdf145e8
commit
a94db35d01
|
@ -28,6 +28,8 @@ import CoreData
|
|||
import Foundation
|
||||
|
||||
extension AppData {
|
||||
|
||||
@MainActor
|
||||
public static let cdProfilesModel: NSManagedObjectModel = {
|
||||
guard let model: NSManagedObjectModel = .mergedModel(from: [.module]) else {
|
||||
fatalError("Unable to build Core Data model (Profiles v3)")
|
||||
|
|
|
@ -56,6 +56,9 @@ extension PassepartoutError: LocalizedError {
|
|||
return Strings.Errors.App.Passepartout.connectionModuleRequired
|
||||
|
||||
case .corruptProviderModule:
|
||||
if let ppReason = reason as? PassepartoutError, ppReason.code == .notFound {
|
||||
return Strings.Errors.App.missingProviderEntity
|
||||
}
|
||||
return Strings.Errors.App.Passepartout.corruptProviderModule(reason?.localizedDescription ?? "")
|
||||
|
||||
case .incompatibleModules:
|
||||
|
|
|
@ -108,6 +108,8 @@ public enum Strings {
|
|||
public static func malformedModule(_ p1: Any, _ p2: Any) -> String {
|
||||
return Strings.tr("Localizable", "errors.app.malformed_module", String(describing: p1), String(describing: p2), fallback: "Module %@ is malformed. %@")
|
||||
}
|
||||
/// No provider server selected.
|
||||
public static let missingProviderEntity = Strings.tr("Localizable", "errors.app.missing_provider_entity", fallback: "No provider server selected.")
|
||||
public enum Passepartout {
|
||||
/// Routing module can only be enabled together with a connection.
|
||||
public static let connectionModuleRequired = Strings.tr("Localizable", "errors.app.passepartout.connection_module_required", fallback: "Routing module can only be enabled together with a connection.")
|
||||
|
|
|
@ -248,6 +248,7 @@
|
|||
|
||||
"errors.app.empty_profile_name" = "Profile name is empty.";
|
||||
"errors.app.malformed_module" = "Module %@ is malformed. %@";
|
||||
"errors.app.missing_provider_entity" = "No provider server selected.";
|
||||
"errors.app.default" = "Unable to complete operation.";
|
||||
"errors.app.passepartout.connection_module_required" = "Routing module can only be enabled together with a connection.";
|
||||
"errors.app.passepartout.corrupt_provider_module" = "Unable to connect to provider server (reason=%@).";
|
||||
|
|
|
@ -85,13 +85,15 @@ private extension AppInlineCoordinator {
|
|||
tunnel: tunnel,
|
||||
registry: registry,
|
||||
isImporting: $isImporting,
|
||||
onEdit: {
|
||||
flow: .init(
|
||||
onEditProfile: {
|
||||
guard let profile = profileManager.profile(withId: $0.id) else {
|
||||
return
|
||||
}
|
||||
enterDetail(of: profile)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func toolbarContent() -> some ToolbarContent {
|
||||
|
@ -143,7 +145,10 @@ private extension AppInlineCoordinator {
|
|||
}
|
||||
|
||||
func enterDetail(of profile: Profile) {
|
||||
profileEditor.editProfile(profile, isShared: profileManager.isRemotelyShared(profileWithId: profile.id))
|
||||
profileEditor.editProfile(
|
||||
profile,
|
||||
isShared: profileManager.isRemotelyShared(profileWithId: profile.id)
|
||||
)
|
||||
push(.editProfile)
|
||||
}
|
||||
|
||||
|
|
|
@ -81,13 +81,15 @@ extension AppModalCoordinator {
|
|||
tunnel: tunnel,
|
||||
registry: registry,
|
||||
isImporting: $isImporting,
|
||||
onEdit: {
|
||||
flow: .init(
|
||||
onEditProfile: {
|
||||
guard let profile = profileManager.profile(withId: $0.id) else {
|
||||
return
|
||||
}
|
||||
enterDetail(of: profile)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
func toolbarContent() -> some ToolbarContent {
|
||||
|
@ -135,7 +137,10 @@ extension AppModalCoordinator {
|
|||
|
||||
func enterDetail(of profile: Profile) {
|
||||
profilePath = NavigationPath()
|
||||
profileEditor.editProfile(profile, isShared: profileManager.isRemotelyShared(profileWithId: profile.id))
|
||||
profileEditor.editProfile(
|
||||
profile,
|
||||
isShared: profileManager.isRemotelyShared(profileWithId: profile.id)
|
||||
)
|
||||
modalRoute = .editProfile
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,11 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
struct ProfileContainerView: View, TunnelInstallationProviding {
|
||||
struct ProfileContainerView: View, Routable, TunnelInstallationProviding {
|
||||
struct Flow {
|
||||
let onEditProfile: (ProfileHeader) -> Void
|
||||
}
|
||||
|
||||
let layout: ProfilesLayout
|
||||
|
||||
let profileManager: ProfileManager
|
||||
|
@ -40,7 +44,7 @@ struct ProfileContainerView: View, TunnelInstallationProviding {
|
|||
@Binding
|
||||
var isImporting: Bool
|
||||
|
||||
let onEdit: (ProfileHeader) -> Void
|
||||
var flow: Flow?
|
||||
|
||||
@StateObject
|
||||
private var interactiveManager = InteractiveManager()
|
||||
|
@ -77,7 +81,7 @@ private extension ProfileContainerView {
|
|||
tunnel: tunnel,
|
||||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
onEdit: onEdit
|
||||
flow: flow
|
||||
)
|
||||
|
||||
case .grid:
|
||||
|
@ -86,7 +90,7 @@ private extension ProfileContainerView {
|
|||
tunnel: tunnel,
|
||||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
onEdit: onEdit
|
||||
flow: flow
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -149,8 +153,7 @@ private struct PreviewView: View {
|
|||
profileManager: .mock,
|
||||
tunnel: .mock,
|
||||
registry: Registry(),
|
||||
isImporting: .constant(false),
|
||||
onEdit: { _ in }
|
||||
isImporting: .constant(false)
|
||||
)
|
||||
}
|
||||
.withMockEnvironment()
|
||||
|
|
|
@ -28,7 +28,7 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
struct ProfileGridView: View, TunnelInstallationProviding {
|
||||
struct ProfileGridView: View, Routable, TunnelInstallationProviding {
|
||||
|
||||
@Environment(\.isSearching)
|
||||
private var isSearching
|
||||
|
@ -43,7 +43,7 @@ struct ProfileGridView: View, TunnelInstallationProviding {
|
|||
|
||||
let errorHandler: ErrorHandler
|
||||
|
||||
let onEdit: (ProfileHeader) -> Void
|
||||
var flow: ProfileContainerView.Flow?
|
||||
|
||||
@State
|
||||
private var nextProfileId: Profile.ID?
|
||||
|
@ -94,9 +94,7 @@ private extension ProfileGridView {
|
|||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
nextProfileId: $nextProfileId,
|
||||
flow: .init(
|
||||
onEditProfile: onEdit
|
||||
)
|
||||
flow: flow
|
||||
)
|
||||
.contextMenu {
|
||||
currentProfile.map {
|
||||
|
@ -107,7 +105,9 @@ private extension ProfileGridView {
|
|||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
isInstalledProfile: true,
|
||||
onEdit: onEdit
|
||||
onEdit: {
|
||||
flow?.onEditProfile($0)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -123,7 +123,9 @@ private extension ProfileGridView {
|
|||
errorHandler: errorHandler,
|
||||
nextProfileId: $nextProfileId,
|
||||
withMarker: true,
|
||||
onEdit: onEdit
|
||||
onEdit: {
|
||||
flow?.onEditProfile($0)
|
||||
}
|
||||
)
|
||||
.themeGridCell(isSelected: header.id == nextProfileId ?? tunnel.currentProfile?.id)
|
||||
.contextMenu {
|
||||
|
@ -134,7 +136,9 @@ private extension ProfileGridView {
|
|||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
isInstalledProfile: false,
|
||||
onEdit: onEdit
|
||||
onEdit: {
|
||||
flow?.onEditProfile($0)
|
||||
}
|
||||
)
|
||||
}
|
||||
.id(header.id)
|
||||
|
@ -148,8 +152,7 @@ private extension ProfileGridView {
|
|||
profileManager: .mock,
|
||||
tunnel: .mock,
|
||||
interactiveManager: InteractiveManager(),
|
||||
errorHandler: .default(),
|
||||
onEdit: { _ in }
|
||||
errorHandler: .default()
|
||||
)
|
||||
.themeWindow(width: 600, height: 300)
|
||||
.withMockEnvironment()
|
||||
|
|
|
@ -28,7 +28,7 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
struct ProfileListView: View, TunnelInstallationProviding {
|
||||
struct ProfileListView: View, Routable, TunnelInstallationProviding {
|
||||
|
||||
@Environment(\.horizontalSizeClass)
|
||||
private var hsClass
|
||||
|
@ -49,7 +49,7 @@ struct ProfileListView: View, TunnelInstallationProviding {
|
|||
|
||||
let errorHandler: ErrorHandler
|
||||
|
||||
let onEdit: (ProfileHeader) -> Void
|
||||
var flow: ProfileContainerView.Flow?
|
||||
|
||||
@State
|
||||
private var nextProfileId: Profile.ID?
|
||||
|
@ -90,9 +90,7 @@ private extension ProfileListView {
|
|||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
nextProfileId: $nextProfileId,
|
||||
flow: .init(
|
||||
onEditProfile: onEdit
|
||||
)
|
||||
flow: flow
|
||||
)
|
||||
.contextMenu {
|
||||
currentProfile.map {
|
||||
|
@ -103,7 +101,9 @@ private extension ProfileListView {
|
|||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
isInstalledProfile: true,
|
||||
onEdit: onEdit
|
||||
onEdit: {
|
||||
flow?.onEditProfile($0)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -119,7 +119,9 @@ private extension ProfileListView {
|
|||
errorHandler: errorHandler,
|
||||
nextProfileId: $nextProfileId,
|
||||
withMarker: true,
|
||||
onEdit: onEdit
|
||||
onEdit: {
|
||||
flow?.onEditProfile($0)
|
||||
}
|
||||
)
|
||||
.contextMenu {
|
||||
ProfileContextMenu(
|
||||
|
@ -129,7 +131,9 @@ private extension ProfileListView {
|
|||
interactiveManager: interactiveManager,
|
||||
errorHandler: errorHandler,
|
||||
isInstalledProfile: false,
|
||||
onEdit: onEdit
|
||||
onEdit: {
|
||||
flow?.onEditProfile($0)
|
||||
}
|
||||
)
|
||||
}
|
||||
.id(header.id)
|
||||
|
@ -153,8 +157,7 @@ private extension ProfileListView {
|
|||
profileManager: .mock,
|
||||
tunnel: .mock,
|
||||
interactiveManager: InteractiveManager(),
|
||||
errorHandler: .default(),
|
||||
onEdit: { _ in }
|
||||
errorHandler: .default()
|
||||
)
|
||||
.withMockEnvironment()
|
||||
}
|
||||
|
|
|
@ -28,13 +28,7 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
extension DNSModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
DNSView(editor: editor, module: self)
|
||||
}
|
||||
}
|
||||
|
||||
private struct DNSView: View {
|
||||
struct DNSView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// DNSModule+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/17/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
|
||||
|
||||
extension DNSModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
DNSView(editor: editor, module: self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// HTTPProxyModule+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/17/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
|
||||
|
||||
extension HTTPProxyModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
HTTPProxyView(editor: editor, module: self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// IPModule+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/17/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
|
||||
|
||||
extension IPModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
IPView(editor: editor, module: self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// OnDemandModule+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/23/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
|
||||
|
||||
extension OnDemandModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
OnDemandView(editor: editor, module: self)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// OpenVPNModule+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 2/17/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
|
||||
|
||||
extension OpenVPNModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
OpenVPNView(editor: editor, module: self)
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPNModule.Builder: InteractiveViewProviding {
|
||||
func interactiveView(with editor: ProfileEditor) -> some View {
|
||||
let draft = editor.binding(forModule: self)
|
||||
|
||||
return OpenVPNView.CredentialsView(
|
||||
isInteractive: draft.isInteractive,
|
||||
credentials: draft.credentials,
|
||||
isAuthenticating: true
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// WireGuardModule+Extensions.swift
|
||||
// Passepartout
|
||||
//
|
||||
// Created by Davide De Rosa on 7/31/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
|
||||
|
||||
extension WireGuardModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
WireGuardView(editor: editor, module: self)
|
||||
}
|
||||
}
|
|
@ -27,13 +27,7 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
extension HTTPProxyModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
HTTPProxyView(editor: editor, module: self)
|
||||
}
|
||||
}
|
||||
|
||||
private struct HTTPProxyView: View {
|
||||
struct HTTPProxyView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
|
|
@ -27,12 +27,6 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
extension IPModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
IPView(editor: editor, module: self)
|
||||
}
|
||||
}
|
||||
|
||||
struct IPView: View {
|
||||
|
||||
@ObservedObject
|
||||
|
|
|
@ -27,13 +27,7 @@ import PassepartoutKit
|
|||
import SwiftUI
|
||||
import UtilsLibrary
|
||||
|
||||
extension OnDemandModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
OnDemandView(editor: editor, module: self)
|
||||
}
|
||||
}
|
||||
|
||||
private struct OnDemandView: View {
|
||||
struct OnDemandView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
private var theme: Theme
|
||||
|
|
|
@ -26,24 +26,6 @@
|
|||
import PassepartoutKit
|
||||
import SwiftUI
|
||||
|
||||
extension OpenVPNModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
OpenVPNView(editor: editor, module: self)
|
||||
}
|
||||
}
|
||||
|
||||
extension OpenVPNModule.Builder: InteractiveViewProviding {
|
||||
func interactiveView(with editor: ProfileEditor) -> some View {
|
||||
let draft = editor.binding(forModule: self)
|
||||
|
||||
return OpenVPNView.CredentialsView(
|
||||
isInteractive: draft.isInteractive,
|
||||
credentials: draft.credentials,
|
||||
isAuthenticating: true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct OpenVPNView: View {
|
||||
|
||||
@ObservedObject
|
||||
|
@ -96,9 +78,8 @@ private extension OpenVPNView {
|
|||
|
||||
var providerModifier: some ViewModifier {
|
||||
VPNProviderContentModifier(
|
||||
providerId: editor.binding(forProviderOf: draft.id),
|
||||
selectedEntity: editor.binding(forProviderEntityOf: draft.id),
|
||||
configurationType: OpenVPN.Configuration.self,
|
||||
providerId: providerId,
|
||||
selectedEntity: providerEntity,
|
||||
isRequired: draft.configurationBuilder == nil,
|
||||
providerRows: {
|
||||
moduleGroup(for: providerAccountRows)
|
||||
|
@ -106,6 +87,14 @@ private extension OpenVPNView {
|
|||
)
|
||||
}
|
||||
|
||||
var providerId: Binding<ProviderID?> {
|
||||
editor.binding(forProviderOf: draft.id)
|
||||
}
|
||||
|
||||
var providerEntity: Binding<VPNEntity<OpenVPN.Configuration>?> {
|
||||
editor.binding(forProviderEntityOf: draft.id)
|
||||
}
|
||||
|
||||
var providerAccountRows: [ModuleRow]? {
|
||||
[.push(caption: Strings.Modules.Openvpn.credentials, route: HashableRoute(Subroute.credentials))]
|
||||
}
|
||||
|
|
|
@ -28,13 +28,7 @@ import PassepartoutKit
|
|||
import PassepartoutWireGuardGo
|
||||
import SwiftUI
|
||||
|
||||
extension WireGuardModule.Builder: ModuleViewProviding {
|
||||
func moduleView(with editor: ProfileEditor) -> some View {
|
||||
WireGuardView(editor: editor, module: self)
|
||||
}
|
||||
}
|
||||
|
||||
private struct WireGuardView: View {
|
||||
struct WireGuardView: View {
|
||||
private enum Subroute: Hashable {
|
||||
case providerServer(id: ProviderID)
|
||||
}
|
||||
|
|
|
@ -39,8 +39,6 @@ struct VPNProviderContentModifier<Configuration, ProviderRows>: ViewModifier whe
|
|||
@Binding
|
||||
var selectedEntity: VPNEntity<Configuration>?
|
||||
|
||||
let configurationType: Configuration.Type
|
||||
|
||||
let isRequired: Bool
|
||||
|
||||
@ViewBuilder
|
||||
|
@ -107,8 +105,7 @@ private extension VPNProviderContentModifier {
|
|||
EmptyView()
|
||||
.modifier(VPNProviderContentModifier(
|
||||
providerId: .constant(.hideme),
|
||||
selectedEntity: .constant(nil),
|
||||
configurationType: OpenVPN.Configuration.self,
|
||||
selectedEntity: .constant(nil as VPNEntity<OpenVPN.Configuration>?),
|
||||
isRequired: false,
|
||||
providerRows: {
|
||||
Text("Other")
|
||||
|
|
|
@ -400,6 +400,29 @@ struct ThemeImageLabel: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct ThemeDisclosableMenu<NameContent, MenuContent>: View where NameContent: View, MenuContent: View {
|
||||
|
||||
@ViewBuilder
|
||||
let name: NameContent
|
||||
|
||||
@ViewBuilder
|
||||
let menu: () -> MenuContent
|
||||
|
||||
var body: some View {
|
||||
Menu(content: menu) {
|
||||
HStack(alignment: .firstTextBaseline) {
|
||||
name
|
||||
ThemeImage(.disclose)
|
||||
}
|
||||
.contentShape(.rect)
|
||||
}
|
||||
.foregroundStyle(.primary)
|
||||
#if os(macOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
struct ThemeCopiableText<Value, ValueView>: View where Value: CustomStringConvertible, ValueView: View {
|
||||
|
||||
@EnvironmentObject
|
||||
|
|
|
@ -33,10 +33,6 @@ struct InstalledProfileView: View, Routable {
|
|||
@EnvironmentObject
|
||||
var theme: Theme
|
||||
|
||||
struct Flow {
|
||||
let onEditProfile: (ProfileHeader) -> Void
|
||||
}
|
||||
|
||||
let layout: ProfilesLayout
|
||||
|
||||
let profileManager: ProfileManager
|
||||
|
@ -52,7 +48,7 @@ struct InstalledProfileView: View, Routable {
|
|||
@Binding
|
||||
var nextProfileId: Profile.ID?
|
||||
|
||||
var flow: Flow?
|
||||
var flow: ProfileContainerView.Flow?
|
||||
|
||||
var body: some View {
|
||||
debugChanges()
|
||||
|
@ -86,21 +82,16 @@ private extension InstalledProfileView {
|
|||
}
|
||||
|
||||
var actionableNameView: some View {
|
||||
Menu(content: menuContent) {
|
||||
HStack(alignment: .firstTextBaseline) {
|
||||
ThemeDisclosableMenu {
|
||||
nameView
|
||||
ThemeImage(.disclose)
|
||||
} menu: {
|
||||
menuContent
|
||||
}
|
||||
.contentShape(.rect)
|
||||
}
|
||||
.foregroundStyle(.primary)
|
||||
#if os(macOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
}
|
||||
|
||||
var nameView: some View {
|
||||
Text(profile?.name ?? Strings.Views.Profiles.Rows.notInstalled)
|
||||
.fixedSize()
|
||||
.font(.title2)
|
||||
.fontWeight(theme.relevantWeight)
|
||||
.themeTruncating(.tail)
|
||||
|
@ -128,7 +119,7 @@ private extension InstalledProfileView {
|
|||
.opacity(installedOpacity)
|
||||
}
|
||||
|
||||
func menuContent() -> some View {
|
||||
var menuContent: some View {
|
||||
ProfileContextMenu(
|
||||
profileManager: profileManager,
|
||||
tunnel: tunnel,
|
||||
|
|
Loading…
Reference in New Issue