From 7b4ffd6516e221067fc788f33629ff25079f3cc9 Mon Sep 17 00:00:00 2001 From: Davide <keeshux@gmail.com> Date: Mon, 10 Feb 2025 22:50:55 +0100 Subject: [PATCH 1/5] Delete profile from editor --- .../EnvironmentValues+Extensions.swift | 13 +++++ .../Views/App/ProfileContextMenu.swift | 3 +- .../Views/App/ProfileRemoveButton.swift | 27 ++++++++-- .../Views/Profile/ModuleViewModifier.swift | 4 +- .../Views/Profile/ProfileActionsSection.swift | 54 +++++++++++++++++++ .../Views/Profile/ProfileCoordinator.swift | 3 ++ .../{UUIDSection.swift => UUIDText.swift} | 22 ++++---- .../Profile/iOS/ProfileEditView+iOS.swift | 7 ++- .../macOS/ProfileGeneralView+macOS.swift | 7 ++- .../macOS/ProfileSplitView+macOS.swift | 4 ++ 10 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift rename Packages/App/Sources/AppUIMain/Views/Profile/{UUIDSection.swift => UUIDText.swift} (72%) diff --git a/Packages/App/Sources/AppUIMain/Extensions/EnvironmentValues+Extensions.swift b/Packages/App/Sources/AppUIMain/Extensions/EnvironmentValues+Extensions.swift index c2680f7dd..cf23fc4ac 100644 --- a/Packages/App/Sources/AppUIMain/Extensions/EnvironmentValues+Extensions.swift +++ b/Packages/App/Sources/AppUIMain/Extensions/EnvironmentValues+Extensions.swift @@ -34,8 +34,21 @@ extension EnvironmentValues { self[NavigationPathKey.self] = newValue } } + + var dismissProfile: () -> Void { + get { + self[DismissProfileKey.self] + } + set { + self[DismissProfileKey.self] = newValue + } + } } private struct NavigationPathKey: EnvironmentKey { static let defaultValue: Binding<NavigationPath> = .constant(NavigationPath()) } + +private struct DismissProfileKey: EnvironmentKey { + static let defaultValue: () -> Void = {} +} diff --git a/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift b/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift index 5b695cb7c..8fd9a5748 100644 --- a/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift +++ b/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift @@ -124,7 +124,8 @@ private extension ProfileContextMenu { var profileRemoveButton: some View { ProfileRemoveButton( profileManager: profileManager, - preview: preview + profileId: preview.id, + profileName: preview.name ) { ThemeImageLabel(Strings.Global.Actions.remove, .contextRemove) } diff --git a/Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift b/Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift index 41f73a386..ada943a55 100644 --- a/Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift +++ b/Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift @@ -24,22 +24,41 @@ // import CommonLibrary +import PassepartoutKit import SwiftUI struct ProfileRemoveButton<Label>: View where Label: View { + + @Environment(\.dismissProfile) + private var dismissProfile + let profileManager: ProfileManager - let preview: ProfilePreview + let profileId: Profile.ID + + let profileName: String let label: () -> Label + @State + private var isConfirming = false + var body: some View { Button(role: .destructive) { - Task { - await profileManager.remove(withId: preview.id) - } + isConfirming = true } label: { label() } + .themeConfirmation( + isPresented: $isConfirming, + title: Strings.Global.Actions.delete, + isDestructive: true, + action: { + Task { + dismissProfile() + await profileManager.remove(withId: profileId) + } + } + ) } } diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/ModuleViewModifier.swift b/Packages/App/Sources/AppUIMain/Views/Profile/ModuleViewModifier.swift index aa326e2dc..6a242437c 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/ModuleViewModifier.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/ModuleViewModifier.swift @@ -42,7 +42,9 @@ struct ModuleViewModifier<T>: ViewModifier where T: ModuleBuilder & Equatable { content #if DEBUG if !isUITesting { - UUIDSection(uuid: draft.id) + Section { + UUIDText(uuid: draft.id) + } } #endif } diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift new file mode 100644 index 000000000..29de715a4 --- /dev/null +++ b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift @@ -0,0 +1,54 @@ +// +// ProfileActionsSection.swift +// Passepartout +// +// Created by Davide De Rosa on 2/10/25. +// Copyright (c) 2025 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 CommonLibrary +import PassepartoutKit +import SwiftUI + +struct ProfileActionsSection: View { + let profileManager: ProfileManager + + let profileEditor: ProfileEditor + + var body: some View { + UUIDText(uuid: profileId) + .asSectionWithTrailingContent { + if profileManager.profile(withId: profileId) != nil { + ProfileRemoveButton( + profileManager: profileManager, + profileId: profileId, + profileName: profileEditor.profile.name, + label: { + Text(Strings.Global.Actions.delete) + } + ) + } + } + } + + private var profileId: Profile.ID { + profileEditor.profile.id + } +} diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift index c6634d714..f2fe53cd0 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileCoordinator.swift @@ -70,6 +70,7 @@ struct ProfileCoordinator: View { var body: some View { contentView .modifier(PaywallModifier(reason: $paywallReason)) + .environment(\.dismissProfile, onDismiss) .withErrorHandler(errorHandler) } } @@ -80,6 +81,7 @@ private extension ProfileCoordinator { var contentView: some View { #if os(iOS) ProfileEditView( + profileManager: profileManager, profileEditor: profileEditor, initialModuleId: initialModuleId, moduleViewFactory: moduleViewFactory, @@ -95,6 +97,7 @@ private extension ProfileCoordinator { .themeNavigationStack(path: $path) #else ProfileSplitView( + profileManager: profileManager, profileEditor: profileEditor, initialModuleId: initialModuleId, moduleViewFactory: moduleViewFactory, diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/UUIDSection.swift b/Packages/App/Sources/AppUIMain/Views/Profile/UUIDText.swift similarity index 72% rename from Packages/App/Sources/AppUIMain/Views/Profile/UUIDSection.swift rename to Packages/App/Sources/AppUIMain/Views/Profile/UUIDText.swift index 5fa5c5122..12ad0ef91 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/UUIDSection.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/UUIDText.swift @@ -1,5 +1,5 @@ // -// UUIDSection.swift +// UUIDText.swift // Passepartout // // Created by Davide De Rosa on 11/4/24. @@ -25,19 +25,17 @@ import SwiftUI -struct UUIDSection: View { +struct UUIDText: View { let uuid: UUID var body: some View { - Section { - ThemeCopiableText( - title: Strings.Unlocalized.uuid, - value: uuid, - valueView: { - Text($0.flatString.localizedDescription(style: .quartets)) - .monospaced() - } - ) - } + ThemeCopiableText( + title: Strings.Unlocalized.uuid, + value: uuid, + valueView: { + Text($0.flatString.localizedDescription(style: .quartets)) + .monospaced() + } + ) } } diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift b/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift index 620e78e51..d8200b1cf 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/iOS/ProfileEditView+iOS.swift @@ -31,6 +31,7 @@ import PassepartoutKit import SwiftUI struct ProfileEditView: View, Routable { + let profileManager: ProfileManager @ObservedObject var profileEditor: ProfileEditor @@ -62,7 +63,10 @@ struct ProfileEditView: View, Routable { paywallReason: $paywallReason ) ProfileBehaviorSection(profileEditor: profileEditor) - UUIDSection(uuid: profileEditor.profile.id) + ProfileActionsSection( + profileManager: profileManager, + profileEditor: profileEditor + ) } .toolbar(content: toolbarContent) .navigationTitle(Strings.Global.Nouns.profile) @@ -187,6 +191,7 @@ private extension ProfileEditView { #Preview { NavigationStack { ProfileEditView( + profileManager: .forPreviews, profileEditor: ProfileEditor(profile: .newMockProfile()), initialModuleId: nil, moduleViewFactory: DefaultModuleViewFactory(registry: Registry()), diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileGeneralView+macOS.swift b/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileGeneralView+macOS.swift index dcecfb120..06f1a5527 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileGeneralView+macOS.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileGeneralView+macOS.swift @@ -29,6 +29,7 @@ import CommonLibrary import SwiftUI struct ProfileGeneralView: View { + let profileManager: ProfileManager @ObservedObject var profileEditor: ProfileEditor @@ -44,7 +45,10 @@ struct ProfileGeneralView: View { paywallReason: $paywallReason ) ProfileBehaviorSection(profileEditor: profileEditor) - UUIDSection(uuid: profileEditor.profile.id) + ProfileActionsSection( + profileManager: profileManager, + profileEditor: profileEditor + ) } .themeForm() } @@ -52,6 +56,7 @@ struct ProfileGeneralView: View { #Preview { ProfileGeneralView( + profileManager: .forPreviews, profileEditor: ProfileEditor(), paywallReason: .constant(nil) ) diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileSplitView+macOS.swift b/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileSplitView+macOS.swift index 34663e464..895ddf665 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileSplitView+macOS.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/macOS/ProfileSplitView+macOS.swift @@ -31,6 +31,8 @@ import PassepartoutKit import SwiftUI struct ProfileSplitView: View, Routable { + let profileManager: ProfileManager + let profileEditor: ProfileEditor let initialModuleId: UUID? @@ -124,6 +126,7 @@ private extension ProfileSplitView { switch detail { case .general: ProfileGeneralView( + profileManager: profileManager, profileEditor: profileEditor, paywallReason: $paywallReason ) @@ -140,6 +143,7 @@ private extension ProfileSplitView { #Preview { ProfileSplitView( + profileManager: .forPreviews, profileEditor: ProfileEditor(profile: .newMockProfile()), initialModuleId: nil, moduleViewFactory: DefaultModuleViewFactory(registry: Registry()), From 699a2db487f269b197ab8319563a1dbf7e06e7a7 Mon Sep 17 00:00:00 2001 From: Davide <keeshux@gmail.com> Date: Mon, 10 Feb 2025 23:33:13 +0100 Subject: [PATCH 2/5] Translate --- .../Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift | 2 +- Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift | 2 ++ .../Sources/UILibrary/Resources/de.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/el.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/en.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/es.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/fr.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/it.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/nl.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/pl.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/pt.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/ru.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/sv.lproj/Localizable.strings | 1 + .../Sources/UILibrary/Resources/uk.lproj/Localizable.strings | 1 + .../UILibrary/Resources/zh-Hans.lproj/Localizable.strings | 1 + 15 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift index 29de715a4..8245f25d9 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift @@ -41,7 +41,7 @@ struct ProfileActionsSection: View { profileId: profileId, profileName: profileEditor.profile.name, label: { - Text(Strings.Global.Actions.delete) + Text(Strings.Views.Profile.Rows.deleteProfile) } ) } diff --git a/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift b/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift index cc22c28dc..57853eee3 100644 --- a/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift +++ b/Packages/App/Sources/UILibrary/L10n/SwiftGen+Strings.swift @@ -863,6 +863,8 @@ public enum Strings { public enum Rows { /// Add module public static let addModule = Strings.tr("Localizable", "views.profile.rows.add_module", fallback: "Add module") + /// Delete profile + public static let deleteProfile = Strings.tr("Localizable", "views.profile.rows.delete_profile", fallback: "Delete profile") } public enum Sections { public enum Name { diff --git a/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings index 01eeed2c6..7a4c7b590 100644 --- a/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/de.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Trotzdem speichern"; "views.profile.module_list.section.footer" = "Tippen Sie auf Module, um ihre Einstellungen zu bearbeiten. Module können gezogen werden, um die Priorität festzulegen."; "views.profile.rows.add_module" = "Modul hinzufügen"; +"views.profile.rows.delete_profile" = "Profil löschen"; "views.profile.sections.name.footer" = "Verwenden Sie diesen Namen, um Ihre VPN-Automationen in der Kurzbefehle-App zu erstellen."; "views.providers.clear_filters" = "Filter löschen"; "views.providers.last_updated" = "Zuletzt aktualisiert am %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings index 293a96a3c..2b63cf074 100644 --- a/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/el.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Αποθήκευση ούτως ή άλλως"; "views.profile.module_list.section.footer" = "Πατήστε σε ένα module για να επεξεργαστείτε τις ρυθμίσεις του. Τα modules μπορούν να μετακινηθούν για να καθορίσετε την προτεραιότητά τους."; "views.profile.rows.add_module" = "Προσθήκη μονάδας"; +"views.profile.rows.delete_profile" = "Διαγραφή προφίλ"; "views.profile.sections.name.footer" = "Χρησιμοποιήστε αυτό το όνομα για να δημιουργήσετε αυτοματισμούς VPN στην εφαρμογή Συντομεύσεις."; "views.providers.clear_filters" = "Καθαρισμός φίλτρων"; "views.providers.last_updated" = "Τελευταία ενημέρωση στις %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings index ab3c90081..649099ace 100644 --- a/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/en.lproj/Localizable.strings @@ -110,6 +110,7 @@ "views.profile.sections.name.footer" = "Use this name to create your VPN automations from the Shortcuts app."; "views.profile.rows.add_module" = "Add module"; +"views.profile.rows.delete_profile" = "Delete profile"; "views.profile.module_list.section.footer" = "Tap modules to edit their settings. Modules may be dragged to determine priority."; "views.profile.alerts.purchase.buttons.ok" = "Save anyway"; diff --git a/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings index 344d60d68..54848c954 100644 --- a/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/es.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Guardar de todos modos"; "views.profile.module_list.section.footer" = "Toca los módulos para editar su configuración. Se pueden arrastrar para establecer la prioridad."; "views.profile.rows.add_module" = "Añadir módulo"; +"views.profile.rows.delete_profile" = "Eliminar perfil"; "views.profile.sections.name.footer" = "Usa este nombre para crear automatizaciones VPN en la app Atajos."; "views.providers.clear_filters" = "Borrar filtros"; "views.providers.last_updated" = "Última actualización el %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings index 83dd5529a..054239173 100644 --- a/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/fr.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Enregistrer quand même"; "views.profile.module_list.section.footer" = "Touchez les modules pour modifier leurs paramètres. Ils peuvent être glissés pour définir leur priorité."; "views.profile.rows.add_module" = "Ajouter un module"; +"views.profile.rows.delete_profile" = "Supprimer le profil"; "views.profile.sections.name.footer" = "Utilisez ce nom pour créer vos automatisations VPN dans l'app Raccourcis."; "views.providers.clear_filters" = "Effacer les filtres"; "views.providers.last_updated" = "Dernière mise à jour le %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings index 125949598..e547c214c 100644 --- a/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/it.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Salva comunque"; "views.profile.module_list.section.footer" = "Tocca i moduli per modificarne le impostazioni. Possono essere trascinati per determinare la priorità."; "views.profile.rows.add_module" = "Aggiungi modulo"; +"views.profile.rows.delete_profile" = "Elimina profilo"; "views.profile.sections.name.footer" = "Usa questo nome per creare le automazioni VPN nell'app Comandi Rapidi."; "views.providers.clear_filters" = "Cancella filtri"; "views.providers.last_updated" = "Ultimo aggiornamento il %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings index 38381a596..e38adf6d6 100644 --- a/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/nl.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Toch opslaan"; "views.profile.module_list.section.footer" = "Tik op modules om hun instellingen te bewerken. Modules kunnen worden versleept om de prioriteit te bepalen."; "views.profile.rows.add_module" = "Module toevoegen"; +"views.profile.rows.delete_profile" = "Verwijder profiel"; "views.profile.sections.name.footer" = "Gebruik deze naam om uw VPN-automatiseringen in de app Opdrachten te maken."; "views.providers.clear_filters" = "Filters wissen"; "views.providers.last_updated" = "Laatst bijgewerkt op %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings index 909da4f25..f59816c87 100644 --- a/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/pl.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Zapisz mimo to"; "views.profile.module_list.section.footer" = "Stuknij moduły, aby edytować ich ustawienia. Można je przeciągnąć, aby ustalić priorytet."; "views.profile.rows.add_module" = "Dodaj moduł"; +"views.profile.rows.delete_profile" = "Usuń profil"; "views.profile.sections.name.footer" = "Użyj tej nazwy, aby tworzyć automatyzacje VPN w aplikacji Skróty."; "views.providers.clear_filters" = "Wyczyść filtry"; "views.providers.last_updated" = "Ostatnia aktualizacja: %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings index a46ad65c2..c247705ee 100644 --- a/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/pt.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Salvar assim mesmo"; "views.profile.module_list.section.footer" = "Toque nos módulos para editar suas configurações. Eles podem ser arrastados para definir a prioridade."; "views.profile.rows.add_module" = "Adicionar módulo"; +"views.profile.rows.delete_profile" = "Excluir perfil"; "views.profile.sections.name.footer" = "Use este nome para criar automações VPN no app Atalhos."; "views.providers.clear_filters" = "Limpar filtros"; "views.providers.last_updated" = "Última atualização em %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings index 8cafd468b..f345df962 100644 --- a/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/ru.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Сохранить в любом случае"; "views.profile.module_list.section.footer" = "Нажмите на модули, чтобы изменить их настройки. Их можно перетаскивать для установки приоритета."; "views.profile.rows.add_module" = "Добавить модуль"; +"views.profile.rows.delete_profile" = "Удалить профиль"; "views.profile.sections.name.footer" = "Используйте это имя для создания автоматизаций VPN в приложении Команды."; "views.providers.clear_filters" = "Очистить фильтры"; "views.providers.last_updated" = "Последнее обновление %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings index 7429c8384..8bb27dc98 100644 --- a/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/sv.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Spara ändå"; "views.profile.module_list.section.footer" = "Tryck på moduler för att redigera inställningarna. Moduler kan dras för att bestämma prioritet."; "views.profile.rows.add_module" = "Lägg till modul"; +"views.profile.rows.delete_profile" = "Ta bort profil"; "views.profile.sections.name.footer" = "Använd detta namn för att skapa VPN-automatiseringar i appen Genvägar."; "views.providers.clear_filters" = "Rensa filter"; "views.providers.last_updated" = "Senast uppdaterad %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings index 57aca8001..fca1d1c67 100644 --- a/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/uk.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "Зберегти все одно"; "views.profile.module_list.section.footer" = "Торкніться модулів, щоб змінити їхні налаштування. Їх можна перетягувати для визначення пріоритету."; "views.profile.rows.add_module" = "Додати модуль"; +"views.profile.rows.delete_profile" = "Видалити профіль"; "views.profile.sections.name.footer" = "Використовуйте цю назву для створення автоматизацій VPN у додатку Команди."; "views.providers.clear_filters" = "Очистити фільтри"; "views.providers.last_updated" = "Останнє оновлення %@"; diff --git a/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings b/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings index 08d33ea62..07cd15f8d 100644 --- a/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings +++ b/Packages/App/Sources/UILibrary/Resources/zh-Hans.lproj/Localizable.strings @@ -284,6 +284,7 @@ "views.profile.alerts.purchase.buttons.ok" = "仍然保存"; "views.profile.module_list.section.footer" = "点击模块以编辑其设置。可以拖动模块来确定优先级。"; "views.profile.rows.add_module" = "添加模块"; +"views.profile.rows.delete_profile" = "删除配置文件"; "views.profile.sections.name.footer" = "使用此名称在快捷指令应用中创建您的 VPN 自动化。"; "views.providers.clear_filters" = "清除筛选"; "views.providers.last_updated" = "最近更新于 %@"; From 384fe21cb611f02823ae759ae5d507fd84199c19 Mon Sep 17 00:00:00 2001 From: Davide <keeshux@gmail.com> Date: Mon, 10 Feb 2025 23:35:03 +0100 Subject: [PATCH 3/5] Add missing hide icon in context menu --- .../Sources/UILibrary/Views/UI/PinActiveProfileToggle.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Packages/App/Sources/UILibrary/Views/UI/PinActiveProfileToggle.swift b/Packages/App/Sources/UILibrary/Views/UI/PinActiveProfileToggle.swift index a8df7faac..da8c27d23 100644 --- a/Packages/App/Sources/UILibrary/Views/UI/PinActiveProfileToggle.swift +++ b/Packages/App/Sources/UILibrary/Views/UI/PinActiveProfileToggle.swift @@ -47,10 +47,12 @@ public struct HideActiveProfileButton: View { } public var body: some View { - Button(Strings.Global.Actions.hide) { + Button { withAnimation { pinsActiveProfile = false } + } label: { + ThemeImageLabel(Strings.Global.Actions.hide, .hide) } } } From c70b45ff1560f34ebf2d5d6e4a3097119050f066 Mon Sep 17 00:00:00 2001 From: Davide <keeshux@gmail.com> Date: Mon, 10 Feb 2025 23:49:09 +0100 Subject: [PATCH 4/5] Split remove buttons Require confirmation in profile. --- .../Views/App/ProfileContextMenu.swift | 10 +-- .../Views/App/ProfileRemoveButton.swift | 64 ------------------- .../Views/Profile/ProfileActionsSection.swift | 39 ++++++++--- 3 files changed, 35 insertions(+), 78 deletions(-) delete mode 100644 Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift diff --git a/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift b/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift index 8fd9a5748..c5130ba89 100644 --- a/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift +++ b/Packages/App/Sources/AppUIMain/Views/App/ProfileContextMenu.swift @@ -122,11 +122,11 @@ private extension ProfileContextMenu { } var profileRemoveButton: some View { - ProfileRemoveButton( - profileManager: profileManager, - profileId: preview.id, - profileName: preview.name - ) { + Button(role: .destructive) { + Task { + await profileManager.remove(withId: preview.id) + } + } label: { ThemeImageLabel(Strings.Global.Actions.remove, .contextRemove) } } diff --git a/Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift b/Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift deleted file mode 100644 index ada943a55..000000000 --- a/Packages/App/Sources/AppUIMain/Views/App/ProfileRemoveButton.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// ProfileRemoveButton.swift -// Passepartout -// -// Created by Davide De Rosa on 9/6/24. -// Copyright (c) 2025 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 CommonLibrary -import PassepartoutKit -import SwiftUI - -struct ProfileRemoveButton<Label>: View where Label: View { - - @Environment(\.dismissProfile) - private var dismissProfile - - let profileManager: ProfileManager - - let profileId: Profile.ID - - let profileName: String - - let label: () -> Label - - @State - private var isConfirming = false - - var body: some View { - Button(role: .destructive) { - isConfirming = true - } label: { - label() - } - .themeConfirmation( - isPresented: $isConfirming, - title: Strings.Global.Actions.delete, - isDestructive: true, - action: { - Task { - dismissProfile() - await profileManager.remove(withId: profileId) - } - } - ) - } -} diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift index 8245f25d9..f5a69c822 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift @@ -28,27 +28,48 @@ import PassepartoutKit import SwiftUI struct ProfileActionsSection: View { + + @Environment(\.dismissProfile) + private var dismissProfile + let profileManager: ProfileManager let profileEditor: ProfileEditor + @State + private var isConfirmingDeletion = false + var body: some View { UUIDText(uuid: profileId) .asSectionWithTrailingContent { if profileManager.profile(withId: profileId) != nil { - ProfileRemoveButton( - profileManager: profileManager, - profileId: profileId, - profileName: profileEditor.profile.name, - label: { - Text(Strings.Views.Profile.Rows.deleteProfile) - } - ) + removeButton + .themeConfirmation( + isPresented: $isConfirmingDeletion, + title: Strings.Global.Actions.delete, + isDestructive: true, + action: { + Task { + dismissProfile() + await profileManager.remove(withId: profileId) + } + } + ) } } } +} + +private extension ProfileActionsSection { + var removeButton: some View { + Button(Strings.Views.Profile.Rows.deleteProfile, role: .destructive) { + isConfirmingDeletion = true + } + } +} - private var profileId: Profile.ID { +private extension ProfileActionsSection { + var profileId: Profile.ID { profileEditor.profile.id } } From 48da2a310bd9060e58a9b4e4b20b1b403773930c Mon Sep 17 00:00:00 2001 From: Davide <keeshux@gmail.com> Date: Mon, 10 Feb 2025 23:53:41 +0100 Subject: [PATCH 5/5] Render differently on iOS/macOS --- .../Views/Profile/ProfileActionsSection.swift | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift index f5a69c822..4d3891dd2 100644 --- a/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift +++ b/Packages/App/Sources/AppUIMain/Views/Profile/ProfileActionsSection.swift @@ -40,27 +40,42 @@ struct ProfileActionsSection: View { private var isConfirmingDeletion = false var body: some View { +#if os(iOS) + Section { + UUIDText(uuid: profileId) + } + Section { + removeContent + .frame(maxWidth: .infinity, alignment: .center) + } +#else UUIDText(uuid: profileId) .asSectionWithTrailingContent { - if profileManager.profile(withId: profileId) != nil { - removeButton - .themeConfirmation( - isPresented: $isConfirmingDeletion, - title: Strings.Global.Actions.delete, - isDestructive: true, - action: { - Task { - dismissProfile() - await profileManager.remove(withId: profileId) - } - } - ) - } + removeContent } +#endif } } private extension ProfileActionsSection { + var removeContent: some View { + profileManager.profile(withId: profileId) + .map { _ in + removeButton + .themeConfirmation( + isPresented: $isConfirmingDeletion, + title: Strings.Global.Actions.delete, + isDestructive: true, + action: { + Task { + dismissProfile() + await profileManager.remove(withId: profileId) + } + } + ) + } + } + var removeButton: some View { Button(Strings.Views.Profile.Rows.deleteProfile, role: .destructive) { isConfirmingDeletion = true