Group profile actions into menus
- Organizer - Duplicate - Profile - Rename - Siri - Uninstall (+ confirmation) - Delete (+ confirmation)
This commit is contained in:
parent
1c047b9ce2
commit
cd854f8ebf
|
@ -34,7 +34,7 @@
|
||||||
0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */; };
|
0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */; };
|
||||||
0E3B7FDA27E51A0200C66F13 /* ProfileView+Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */; };
|
0E3B7FDA27E51A0200C66F13 /* ProfileView+Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */; };
|
||||||
0E3CD47F280DA14B007075C0 /* OrganizerView+AddMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD47E280DA14B007075C0 /* OrganizerView+AddMenu.swift */; };
|
0E3CD47F280DA14B007075C0 /* OrganizerView+AddMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD47E280DA14B007075C0 /* OrganizerView+AddMenu.swift */; };
|
||||||
0E3CD483280DAE92007075C0 /* ProfileView+Toolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD482280DAE92007075C0 /* ProfileView+Toolbar.swift */; };
|
0E3CD483280DAE92007075C0 /* ProfileView+MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CD482280DAE92007075C0 /* ProfileView+MainMenu.swift */; };
|
||||||
0E44689627B051C300A14CE4 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689527B051C300A14CE4 /* ProfileView.swift */; };
|
0E44689627B051C300A14CE4 /* ProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689527B051C300A14CE4 /* ProfileView.swift */; };
|
||||||
0E44689C27B11B5300A14CE4 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689B27B11B5300A14CE4 /* AboutView.swift */; };
|
0E44689C27B11B5300A14CE4 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E44689B27B11B5300A14CE4 /* AboutView.swift */; };
|
||||||
0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */; };
|
0E49F6BB27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */; };
|
||||||
|
@ -64,7 +64,6 @@
|
||||||
0E71ACFB27C12E5300F85C4B /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFA27C12E5300F85C4B /* VersionView.swift */; };
|
0E71ACFB27C12E5300F85C4B /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFA27C12E5300F85C4B /* VersionView.swift */; };
|
||||||
0E71ACFD27C1321A00F85C4B /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFC27C1321A00F85C4B /* ActivityView.swift */; };
|
0E71ACFD27C1321A00F85C4B /* ActivityView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E71ACFC27C1321A00F85C4B /* ActivityView.swift */; };
|
||||||
0E7577D72816A3B200081CBE /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577D62816A3B200081CBE /* DestructiveButton.swift */; };
|
0E7577D72816A3B200081CBE /* DestructiveButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577D62816A3B200081CBE /* DestructiveButton.swift */; };
|
||||||
0E7577DD2816C3AD00081CBE /* ProfileView+Buttons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577DC2816C3AD00081CBE /* ProfileView+Buttons.swift */; };
|
|
||||||
0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577DE2817E22C00081CBE /* VPNToggle.swift */; };
|
0E7577DF2817E22C00081CBE /* VPNToggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E7577DE2817E22C00081CBE /* VPNToggle.swift */; };
|
||||||
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */; };
|
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */; };
|
||||||
0E92D7C627F103300033CB7B /* ProfileView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */; };
|
0E92D7C627F103300033CB7B /* ProfileView+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */; };
|
||||||
|
@ -220,7 +219,7 @@
|
||||||
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = "<group>"; };
|
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = "<group>"; };
|
||||||
0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Provider.swift"; sourceTree = "<group>"; };
|
0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Provider.swift"; sourceTree = "<group>"; };
|
||||||
0E3CD47E280DA14B007075C0 /* OrganizerView+AddMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+AddMenu.swift"; sourceTree = "<group>"; };
|
0E3CD47E280DA14B007075C0 /* OrganizerView+AddMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+AddMenu.swift"; sourceTree = "<group>"; };
|
||||||
0E3CD482280DAE92007075C0 /* ProfileView+Toolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Toolbar.swift"; sourceTree = "<group>"; };
|
0E3CD482280DAE92007075C0 /* ProfileView+MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+MainMenu.swift"; sourceTree = "<group>"; };
|
||||||
0E44689527B051C300A14CE4 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
0E44689527B051C300A14CE4 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
|
||||||
0E44689B27B11B5300A14CE4 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
0E44689B27B11B5300A14CE4 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
|
||||||
0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+OpenVPN.swift"; sourceTree = "<group>"; };
|
0E49F6BA27D7638300385834 /* EndpointAdvancedView+OpenVPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EndpointAdvancedView+OpenVPN.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -251,7 +250,6 @@
|
||||||
0E71ACFA27C12E5300F85C4B /* VersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionView.swift; sourceTree = "<group>"; };
|
0E71ACFA27C12E5300F85C4B /* VersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionView.swift; sourceTree = "<group>"; };
|
||||||
0E71ACFC27C1321A00F85C4B /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = "<group>"; };
|
0E71ACFC27C1321A00F85C4B /* ActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityView.swift; sourceTree = "<group>"; };
|
||||||
0E7577D62816A3B200081CBE /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = "<group>"; };
|
0E7577D62816A3B200081CBE /* DestructiveButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DestructiveButton.swift; sourceTree = "<group>"; };
|
||||||
0E7577DC2816C3AD00081CBE /* ProfileView+Buttons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Buttons.swift"; sourceTree = "<group>"; };
|
|
||||||
0E7577DE2817E22C00081CBE /* VPNToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNToggle.swift; sourceTree = "<group>"; };
|
0E7577DE2817E22C00081CBE /* VPNToggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNToggle.swift; sourceTree = "<group>"; };
|
||||||
0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostViewModel.swift; sourceTree = "<group>"; };
|
0E90DFE527BACC1500EF5078 /* AddHostViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddHostViewModel.swift; sourceTree = "<group>"; };
|
||||||
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Configuration.swift"; sourceTree = "<group>"; };
|
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+Configuration.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -454,13 +452,12 @@
|
||||||
0ED30DD127EA1F650057D8A3 /* PaywallView+Purchase.swift */,
|
0ED30DD127EA1F650057D8A3 /* PaywallView+Purchase.swift */,
|
||||||
0ED89C2427DE45A3008B36D6 /* ProfileHeaderRow.swift */,
|
0ED89C2427DE45A3008B36D6 /* ProfileHeaderRow.swift */,
|
||||||
0E44689527B051C300A14CE4 /* ProfileView.swift */,
|
0E44689527B051C300A14CE4 /* ProfileView.swift */,
|
||||||
0E7577DC2816C3AD00081CBE /* ProfileView+Buttons.swift */,
|
|
||||||
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */,
|
0E92D7C527F103300033CB7B /* ProfileView+Configuration.swift */,
|
||||||
0E92D7F327F104B80033CB7B /* ProfileView+Diagnostics.swift */,
|
0E92D7F327F104B80033CB7B /* ProfileView+Diagnostics.swift */,
|
||||||
0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */,
|
0E92D7C827F1042A0033CB7B /* ProfileView+Extra.swift */,
|
||||||
|
0E3CD482280DAE92007075C0 /* ProfileView+MainMenu.swift */,
|
||||||
0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */,
|
0E3B7FD927E51A0200C66F13 /* ProfileView+Provider.swift */,
|
||||||
0EBC074B27EB673C00208AD9 /* ProfileView+Rename.swift */,
|
0EBC074B27EB673C00208AD9 /* ProfileView+Rename.swift */,
|
||||||
0E3CD482280DAE92007075C0 /* ProfileView+Toolbar.swift */,
|
|
||||||
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */,
|
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */,
|
||||||
0E71ACF027C1073800F85C4B /* ProviderLocationView.swift */,
|
0E71ACF027C1073800F85C4B /* ProviderLocationView.swift */,
|
||||||
0E71ACEE27C106B400F85C4B /* ProviderPresetView.swift */,
|
0E71ACEE27C106B400F85C4B /* ProviderPresetView.swift */,
|
||||||
|
@ -902,7 +899,6 @@
|
||||||
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */,
|
0E90DFE627BACC1500EF5078 /* AddHostViewModel.swift in Sources */,
|
||||||
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */,
|
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */,
|
||||||
0EF708322811CC8400A3A308 /* VPNStatusText.swift in Sources */,
|
0EF708322811CC8400A3A308 /* VPNStatusText.swift in Sources */,
|
||||||
0E7577DD2816C3AD00081CBE /* ProfileView+Buttons.swift in Sources */,
|
|
||||||
0E5324A627D297BB002565C3 /* InApp.swift in Sources */,
|
0E5324A627D297BB002565C3 /* InApp.swift in Sources */,
|
||||||
0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */,
|
0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */,
|
||||||
0E7577D72816A3B200081CBE /* DestructiveButton.swift in Sources */,
|
0E7577D72816A3B200081CBE /* DestructiveButton.swift in Sources */,
|
||||||
|
@ -967,7 +963,7 @@
|
||||||
0E71ACF927C12E4800F85C4B /* CreditsView.swift in Sources */,
|
0E71ACF927C12E4800F85C4B /* CreditsView.swift in Sources */,
|
||||||
0ED89C1527DE0A0C008B36D6 /* Shortcut.swift in Sources */,
|
0ED89C1527DE0A0C008B36D6 /* Shortcut.swift in Sources */,
|
||||||
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */,
|
0E34A2B927CAA96A00C73B67 /* OpenVPN+L10n.swift in Sources */,
|
||||||
0E3CD483280DAE92007075C0 /* ProfileView+Toolbar.swift in Sources */,
|
0E3CD483280DAE92007075C0 /* ProfileView+MainMenu.swift in Sources */,
|
||||||
0EB17EAE27D226CF00D473B5 /* LocalProduct.swift in Sources */,
|
0EB17EAE27D226CF00D473B5 /* LocalProduct.swift in Sources */,
|
||||||
0E71ACEB27C1060D00F85C4B /* EndpointView.swift in Sources */,
|
0E71ACEB27C1060D00F85C4B /* EndpointView.swift in Sources */,
|
||||||
0E53249927D26B51002565C3 /* ProductManager.swift in Sources */,
|
0E53249927D26B51002565C3 /* ProductManager.swift in Sources */,
|
||||||
|
|
|
@ -126,7 +126,7 @@
|
||||||
<EnvironmentVariables>
|
<EnvironmentVariables>
|
||||||
<EnvironmentVariable
|
<EnvironmentVariable
|
||||||
key = "APP_TYPE"
|
key = "APP_TYPE"
|
||||||
value = "0"
|
value = "2"
|
||||||
isEnabled = "YES">
|
isEnabled = "YES">
|
||||||
</EnvironmentVariable>
|
</EnvironmentVariable>
|
||||||
<EnvironmentVariable
|
<EnvironmentVariable
|
||||||
|
|
|
@ -437,6 +437,8 @@ internal enum L10n {
|
||||||
internal static let configuration = L10n.tr("Localizable", "global.strings.configuration")
|
internal static let configuration = L10n.tr("Localizable", "global.strings.configuration")
|
||||||
/// Default
|
/// Default
|
||||||
internal static let `default` = L10n.tr("Localizable", "global.strings.default")
|
internal static let `default` = L10n.tr("Localizable", "global.strings.default")
|
||||||
|
/// Delete
|
||||||
|
internal static let delete = L10n.tr("Localizable", "global.strings.delete")
|
||||||
/// Disabled
|
/// Disabled
|
||||||
internal static let disabled = L10n.tr("Localizable", "global.strings.disabled")
|
internal static let disabled = L10n.tr("Localizable", "global.strings.disabled")
|
||||||
/// Domain
|
/// Domain
|
||||||
|
@ -493,6 +495,8 @@ internal enum L10n {
|
||||||
internal static let servers = L10n.tr("Localizable", "global.strings.servers")
|
internal static let servers = L10n.tr("Localizable", "global.strings.servers")
|
||||||
/// Translations
|
/// Translations
|
||||||
internal static let translations = L10n.tr("Localizable", "global.strings.translations")
|
internal static let translations = L10n.tr("Localizable", "global.strings.translations")
|
||||||
|
/// Uninstall
|
||||||
|
internal static let uninstall = L10n.tr("Localizable", "global.strings.uninstall")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,8 +698,6 @@ internal enum L10n {
|
||||||
internal static func message(_ p1: Any) -> String {
|
internal static func message(_ p1: Any) -> String {
|
||||||
return L10n.tr("Localizable", "organizer.alerts.remove_profile.message", String(describing: p1))
|
return L10n.tr("Localizable", "organizer.alerts.remove_profile.message", String(describing: p1))
|
||||||
}
|
}
|
||||||
/// Remove profile
|
|
||||||
internal static let title = L10n.tr("Localizable", "organizer.alerts.remove_profile.title")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
internal enum Empty {
|
internal enum Empty {
|
||||||
|
@ -815,10 +817,6 @@ internal enum L10n {
|
||||||
/// Reconnect
|
/// Reconnect
|
||||||
internal static let caption = L10n.tr("Localizable", "profile.items.reconnect.caption")
|
internal static let caption = L10n.tr("Localizable", "profile.items.reconnect.caption")
|
||||||
}
|
}
|
||||||
internal enum Uninstall {
|
|
||||||
/// Remove VPN configuration
|
|
||||||
internal static let caption = L10n.tr("Localizable", "profile.items.uninstall.caption")
|
|
||||||
}
|
|
||||||
internal enum UseProfile {
|
internal enum UseProfile {
|
||||||
/// Use this profile
|
/// Use this profile
|
||||||
internal static let caption = L10n.tr("Localizable", "profile.items.use_profile.caption")
|
internal static let caption = L10n.tr("Localizable", "profile.items.use_profile.caption")
|
||||||
|
|
|
@ -172,6 +172,10 @@ extension View {
|
||||||
"xmark"
|
"xmark"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var themeUninstallImage: String {
|
||||||
|
"arrow.uturn.down"
|
||||||
|
}
|
||||||
|
|
||||||
var themeDeleteImage: String {
|
var themeDeleteImage: String {
|
||||||
"trash.fill"
|
"trash.fill"
|
||||||
}
|
}
|
||||||
|
@ -281,10 +285,6 @@ extension View {
|
||||||
foregroundColor(themeLightTextColor)
|
foregroundColor(themeLightTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
func themeDestructiveButtonStyle() -> some View {
|
|
||||||
foregroundColor(themeErrorColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 15, *)
|
@available(iOS 15, *)
|
||||||
func themePrimaryTintStyle() -> some View {
|
func themePrimaryTintStyle() -> some View {
|
||||||
tint(themePrimaryBackgroundColor)
|
tint(themePrimaryBackgroundColor)
|
||||||
|
|
|
@ -109,11 +109,7 @@ extension OrganizerView {
|
||||||
isActive: profileManager.isActiveProfile(header.id)
|
isActive: profileManager.isActiveProfile(header.id)
|
||||||
)
|
)
|
||||||
}.contextMenu {
|
}.contextMenu {
|
||||||
Button {
|
ProfileView.DuplicateButton(header: header)
|
||||||
duplicateProfile(withId: header.id)
|
|
||||||
} label: {
|
|
||||||
Label(L10n.Global.Strings.duplicate, systemImage: themeDuplicateImage)
|
|
||||||
}
|
|
||||||
}.themeTextButtonStyle()
|
}.themeTextButtonStyle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,10 +190,6 @@ extension OrganizerView.ProfilesList {
|
||||||
profileManager.removeProfiles(withIds: toDelete)
|
profileManager.removeProfiles(withIds: toDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func duplicateProfile(withId id: UUID) {
|
|
||||||
profileManager.duplicateProfile(withId: id)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func performMigrationsIfNeeded() {
|
private func performMigrationsIfNeeded() {
|
||||||
Task {
|
Task {
|
||||||
await appManager.doMigrations(profileManager)
|
await appManager.doMigrations(profileManager)
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
//
|
|
||||||
// ProfileView+Buttons.swift
|
|
||||||
// Passepartout
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 4/25/22.
|
|
||||||
// Copyright (c) 2022 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
|
|
||||||
import PassepartoutCore
|
|
||||||
|
|
||||||
extension ProfileView {
|
|
||||||
struct RemoveProfileButton: View {
|
|
||||||
@ObservedObject private var profileManager: ProfileManager
|
|
||||||
|
|
||||||
private let header: Profile.Header
|
|
||||||
|
|
||||||
@State private var isConfirming = false
|
|
||||||
|
|
||||||
private let title = L10n.Organizer.Alerts.RemoveProfile.title
|
|
||||||
|
|
||||||
init(header: Profile.Header) {
|
|
||||||
profileManager = .shared
|
|
||||||
self.header = header
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
DestructiveButton {
|
|
||||||
isConfirming = true
|
|
||||||
} label: {
|
|
||||||
Label(title, systemImage: themeDeleteImage)
|
|
||||||
}.actionSheet(isPresented: $isConfirming) {
|
|
||||||
ActionSheet(
|
|
||||||
title: Text(L10n.Organizer.Alerts.RemoveProfile.message(header.name)),
|
|
||||||
message: nil,
|
|
||||||
buttons: [
|
|
||||||
.destructive(Text(title), action: removeProfile),
|
|
||||||
.cancel(Text(L10n.Global.Strings.cancel))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}.themeDestructiveButtonStyle()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func removeProfile() {
|
|
||||||
profileManager.removeProfiles(withIds: [header.id])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UninstallVPNButton: View {
|
|
||||||
@ObservedObject private var vpnManager: VPNManager
|
|
||||||
|
|
||||||
@State private var isConfirming = false
|
|
||||||
|
|
||||||
init() {
|
|
||||||
vpnManager = .shared
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
DestructiveButton {
|
|
||||||
isConfirming = true
|
|
||||||
} label: {
|
|
||||||
Label(L10n.Profile.Items.Uninstall.caption, systemImage: themeDeleteImage)
|
|
||||||
}.actionSheet(isPresented: $isConfirming) {
|
|
||||||
ActionSheet(
|
|
||||||
title: Text(L10n.Profile.Alerts.UninstallVpn.message),
|
|
||||||
message: nil,
|
|
||||||
buttons: [
|
|
||||||
.destructive(Text(L10n.Profile.Items.Uninstall.caption), action: uninstallVPN),
|
|
||||||
.cancel(Text(L10n.Global.Strings.cancel))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}.themeDestructiveButtonStyle()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func uninstallVPN() {
|
|
||||||
Task {
|
|
||||||
await vpnManager.uninstall()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
//
|
||||||
|
// ProfileView+MainMenu.swift
|
||||||
|
// Passepartout
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 2/6/22.
|
||||||
|
// Copyright (c) 2022 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
|
||||||
|
import PassepartoutCore
|
||||||
|
|
||||||
|
extension ProfileView {
|
||||||
|
struct MainMenu: View {
|
||||||
|
enum ActionSheetType: Int, Identifiable {
|
||||||
|
case uninstallVPN
|
||||||
|
|
||||||
|
case deleteProfile
|
||||||
|
|
||||||
|
var id: Int {
|
||||||
|
rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObservedObject private var profileManager: ProfileManager
|
||||||
|
|
||||||
|
@ObservedObject private var vpnManager: VPNManager
|
||||||
|
|
||||||
|
@ObservedObject private var currentProfile: ObservableProfile
|
||||||
|
|
||||||
|
private var header: Profile.Header {
|
||||||
|
currentProfile.value.header
|
||||||
|
}
|
||||||
|
|
||||||
|
@Binding private var modalType: ModalType?
|
||||||
|
|
||||||
|
@State private var actionSheetType: ActionSheetType?
|
||||||
|
|
||||||
|
private let uninstallVPNTitle = L10n.Global.Strings.uninstall
|
||||||
|
|
||||||
|
private let deleteProfileTitle = L10n.Global.Strings.delete
|
||||||
|
|
||||||
|
private let cancelTitle = L10n.Global.Strings.cancel
|
||||||
|
|
||||||
|
init(currentProfile: ObservableProfile, modalType: Binding<ModalType?>) {
|
||||||
|
profileManager = .shared
|
||||||
|
vpnManager = .shared
|
||||||
|
self.currentProfile = currentProfile
|
||||||
|
_modalType = modalType
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Menu {
|
||||||
|
RenameButton(
|
||||||
|
modalType: $modalType
|
||||||
|
)
|
||||||
|
// should contextually set duplicate as current profile
|
||||||
|
// DuplicateButton(
|
||||||
|
// header: currentProfile.value.header
|
||||||
|
// )
|
||||||
|
ShortcutsButton(
|
||||||
|
modalType: $modalType
|
||||||
|
)
|
||||||
|
uninstallVPNButton
|
||||||
|
Divider()
|
||||||
|
deleteProfileButton
|
||||||
|
} label: {
|
||||||
|
themeSettingsMenuImage.asSystemImage
|
||||||
|
}.actionSheet(item: $actionSheetType) {
|
||||||
|
switch $0 {
|
||||||
|
case .uninstallVPN:
|
||||||
|
return ActionSheet(
|
||||||
|
title: Text(L10n.Profile.Alerts.UninstallVpn.message),
|
||||||
|
message: nil,
|
||||||
|
buttons: [
|
||||||
|
.destructive(Text(uninstallVPNTitle), action: uninstallVPN),
|
||||||
|
.cancel(Text(cancelTitle))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
case .deleteProfile:
|
||||||
|
return ActionSheet(
|
||||||
|
title: Text(L10n.Organizer.Alerts.RemoveProfile.message(header.name)),
|
||||||
|
message: nil,
|
||||||
|
buttons: [
|
||||||
|
.destructive(Text(deleteProfileTitle), action: removeProfile),
|
||||||
|
.cancel(Text(cancelTitle))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var uninstallVPNButton: some View {
|
||||||
|
Button {
|
||||||
|
actionSheetType = .uninstallVPN
|
||||||
|
} label: {
|
||||||
|
Label(uninstallVPNTitle, systemImage: themeUninstallImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var deleteProfileButton: some View {
|
||||||
|
DestructiveButton {
|
||||||
|
actionSheetType = .deleteProfile
|
||||||
|
} label: {
|
||||||
|
Label(deleteProfileTitle, systemImage: themeDeleteImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func uninstallVPN() {
|
||||||
|
Task {
|
||||||
|
await vpnManager.uninstall()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func removeProfile() {
|
||||||
|
profileManager.removeProfiles(withIds: [header.id])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RenameButton: View {
|
||||||
|
@Binding private var modalType: ModalType?
|
||||||
|
|
||||||
|
init(modalType: Binding<ModalType?>) {
|
||||||
|
_modalType = modalType
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button {
|
||||||
|
modalType = .rename
|
||||||
|
} label: {
|
||||||
|
Label(L10n.Global.Strings.rename, systemImage: themeRenameProfileImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ShortcutsButton: View {
|
||||||
|
@ObservedObject private var productManager: ProductManager
|
||||||
|
|
||||||
|
@Binding private var modalType: ModalType?
|
||||||
|
|
||||||
|
init(modalType: Binding<ModalType?>) {
|
||||||
|
productManager = .shared
|
||||||
|
_modalType = modalType
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isEligibleForSiri: Bool {
|
||||||
|
productManager.isEligible(forFeature: .siriShortcuts)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button {
|
||||||
|
presentShortcutsOrPaywall()
|
||||||
|
} label: {
|
||||||
|
Label(Unlocalized.Other.siri, systemImage: themeShortcutsImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentShortcutsOrPaywall() {
|
||||||
|
|
||||||
|
// eligibility: enter Siri shortcuts or present paywall
|
||||||
|
if isEligibleForSiri {
|
||||||
|
modalType = .shortcuts
|
||||||
|
} else {
|
||||||
|
modalType = .paywallShortcuts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DuplicateButton: View {
|
||||||
|
@ObservedObject private var profileManager: ProfileManager
|
||||||
|
|
||||||
|
let header: Profile.Header
|
||||||
|
|
||||||
|
init(header: Profile.Header) {
|
||||||
|
profileManager = .shared
|
||||||
|
self.header = header
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Button {
|
||||||
|
duplicateProfile(withId: header.id)
|
||||||
|
} label: {
|
||||||
|
Label(L10n.Global.Strings.duplicate, systemImage: themeDuplicateImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func duplicateProfile(withId id: UUID) {
|
||||||
|
profileManager.duplicateProfile(withId: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,81 +0,0 @@
|
||||||
//
|
|
||||||
// ProfileView+Toolbar.swift
|
|
||||||
// Passepartout
|
|
||||||
//
|
|
||||||
// Created by Davide De Rosa on 2/6/22.
|
|
||||||
// Copyright (c) 2022 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
|
|
||||||
import PassepartoutCore
|
|
||||||
|
|
||||||
extension ProfileView {
|
|
||||||
struct ShortcutsItem: View {
|
|
||||||
@ObservedObject private var productManager: ProductManager
|
|
||||||
|
|
||||||
@Binding private var modalType: ModalType?
|
|
||||||
|
|
||||||
init(modalType: Binding<ModalType?>) {
|
|
||||||
productManager = .shared
|
|
||||||
_modalType = modalType
|
|
||||||
}
|
|
||||||
|
|
||||||
private var isEligibleForSiri: Bool {
|
|
||||||
productManager.isEligible(forFeature: .siriShortcuts)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Button {
|
|
||||||
presentShortcutsOrPaywall()
|
|
||||||
} label: {
|
|
||||||
themeShortcutsImage.asSystemImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func presentShortcutsOrPaywall() {
|
|
||||||
|
|
||||||
// eligibility: enter Siri shortcuts or present paywall
|
|
||||||
if isEligibleForSiri {
|
|
||||||
modalType = .shortcuts
|
|
||||||
} else {
|
|
||||||
modalType = .paywallShortcuts
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RenameItem: View {
|
|
||||||
@ObservedObject private var currentProfile: ObservableProfile
|
|
||||||
|
|
||||||
@Binding private var modalType: ModalType?
|
|
||||||
|
|
||||||
init(currentProfile: ObservableProfile, modalType: Binding<ModalType?>) {
|
|
||||||
self.currentProfile = currentProfile
|
|
||||||
_modalType = modalType
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
Button {
|
|
||||||
modalType = .rename
|
|
||||||
} label: {
|
|
||||||
themeRenameProfileImage.asSystemImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -70,16 +70,10 @@ struct ProfileView: View {
|
||||||
WelcomeView()
|
WelcomeView()
|
||||||
}
|
}
|
||||||
}.toolbar {
|
}.toolbar {
|
||||||
ToolbarItemGroup(placement: .navigationBarTrailing) {
|
MainMenu(
|
||||||
ShortcutsItem(
|
currentProfile: profileManager.currentProfile,
|
||||||
modalType: $modalType
|
modalType: $modalType
|
||||||
).disabled(!isExisting)
|
).disabled(!isExisting)
|
||||||
|
|
||||||
RenameItem(
|
|
||||||
currentProfile: profileManager.currentProfile,
|
|
||||||
modalType: $modalType
|
|
||||||
).disabled(!isExisting)
|
|
||||||
}
|
|
||||||
}.sheet(item: $modalType, content: presentedModal)
|
}.sheet(item: $modalType, content: presentedModal)
|
||||||
.navigationTitle(title)
|
.navigationTitle(title)
|
||||||
.themeSecondaryView()
|
.themeSecondaryView()
|
||||||
|
@ -103,10 +97,6 @@ struct ProfileView: View {
|
||||||
)
|
)
|
||||||
ExtraSection(currentProfile: profileManager.currentProfile)
|
ExtraSection(currentProfile: profileManager.currentProfile)
|
||||||
DiagnosticsSection(currentProfile: profileManager.currentProfile)
|
DiagnosticsSection(currentProfile: profileManager.currentProfile)
|
||||||
Section {
|
|
||||||
UninstallVPNButton()
|
|
||||||
RemoveProfileButton(header: profileManager.currentProfile.value.header)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
"global.strings.rename" = "Rename";
|
"global.strings.rename" = "Rename";
|
||||||
"global.strings.duplicate" = "Duplicate";
|
"global.strings.duplicate" = "Duplicate";
|
||||||
"global.strings.add" = "Add";
|
"global.strings.add" = "Add";
|
||||||
|
"global.strings.delete" = "Delete";
|
||||||
|
"global.strings.uninstall" = "Uninstall";
|
||||||
"global.strings.default" = "Default";
|
"global.strings.default" = "Default";
|
||||||
"global.strings.name" = "Name";
|
"global.strings.name" = "Name";
|
||||||
"global.strings.profiles" = "Profiles";
|
"global.strings.profiles" = "Profiles";
|
||||||
|
@ -123,7 +125,6 @@
|
||||||
"organizer.alerts.reddit.buttons.remind" = "Remind me later";
|
"organizer.alerts.reddit.buttons.remind" = "Remind me later";
|
||||||
"organizer.alerts.reddit.buttons.never" = "Don't ask again";
|
"organizer.alerts.reddit.buttons.never" = "Don't ask again";
|
||||||
|
|
||||||
"organizer.alerts.remove_profile.title" = "Remove profile";
|
|
||||||
"organizer.alerts.remove_profile.message" = "Are you sure you want to delete profile %@?";
|
"organizer.alerts.remove_profile.message" = "Are you sure you want to delete profile %@?";
|
||||||
|
|
||||||
/* MARK: AddProfileView */
|
/* MARK: AddProfileView */
|
||||||
|
@ -164,7 +165,6 @@
|
||||||
"profile.items.vpn_survives_sleep.caption" = "Keep alive on sleep";
|
"profile.items.vpn_survives_sleep.caption" = "Keep alive on sleep";
|
||||||
"profile.items.vpn_resolves_hostname.caption" = "Resolve provider hostname";
|
"profile.items.vpn_resolves_hostname.caption" = "Resolve provider hostname";
|
||||||
"profile.items.reconnect.caption" = "Reconnect";
|
"profile.items.reconnect.caption" = "Reconnect";
|
||||||
"profile.items.uninstall.caption" = "Remove VPN configuration";
|
|
||||||
|
|
||||||
"profile.alerts.rename.title" = "Rename profile";
|
"profile.alerts.rename.title" = "Rename profile";
|
||||||
"profile.alerts.reconnect_vpn.message" = "Do you want to reconnect to the VPN?";
|
"profile.alerts.reconnect_vpn.message" = "Do you want to reconnect to the VPN?";
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 222 KiB |
Loading…
Reference in New Issue