diff --git a/Passepartout/App/AppDelegate.swift b/Passepartout/App/AppDelegate.swift index 7d897c32..02cdf4e2 100644 --- a/Passepartout/App/AppDelegate.swift +++ b/Passepartout/App/AppDelegate.swift @@ -33,21 +33,8 @@ final class AppDelegate: NSObject, UIApplicationDelegate { #else import AppKit -import CommonLibrary -import PassepartoutKit final class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(_ notification: Notification) { - NSWindow.allowsAutomaticWindowTabbing = false - - // XXX: hack to only retain "Edit" menu - NSApp.mainMenu?.items = NSApp.mainMenu?.items.filter { - [BundleConfiguration.main.displayName, "Edit"].contains($0.title) - } ?? [] - } - - func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - true - } } + #endif diff --git a/Passepartout/App/PassepartoutApp.swift b/Passepartout/App/PassepartoutApp.swift index a7e5de8d..05414538 100644 --- a/Passepartout/App/PassepartoutApp.swift +++ b/Passepartout/App/PassepartoutApp.swift @@ -53,6 +53,11 @@ struct PassepartoutApp: App { #else Window(appName, id: appName, content: content) .defaultSize(width: 600.0, height: 400.0) + +// Settings { +// SettingsView() +// .frame(minWidth: 300, minHeight: 100) +// } #endif } } diff --git a/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift b/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift index a4a52feb..ab3dc25b 100644 --- a/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift +++ b/Passepartout/Library/Sources/AppUI/L10n/SwiftGen+Strings.swift @@ -139,6 +139,8 @@ internal enum Strings { } } internal enum Global { + /// About + internal static let about = Strings.tr("Localizable", "global.about", fallback: "About") /// Account internal static let account = Strings.tr("Localizable", "global.account", fallback: "Account") /// Address @@ -397,46 +399,44 @@ internal enum Strings { } } internal enum Views { - internal enum Advanced { - /// Lock app access - internal static let lockInBackground = Strings.tr("Localizable", "views.advanced.lock_in_background", fallback: "Lock app access") - /// Advanced - internal static let title = Strings.tr("Localizable", "views.advanced.title", fallback: "Advanced") + internal enum About { + /// About + internal static let title = Strings.tr("Localizable", "views.about.title", fallback: "About") internal enum Credits { /// Licenses - internal static let licenses = Strings.tr("Localizable", "views.advanced.credits.licenses", fallback: "Licenses") + internal static let licenses = Strings.tr("Localizable", "views.about.credits.licenses", fallback: "Licenses") /// Notices - internal static let notices = Strings.tr("Localizable", "views.advanced.credits.notices", fallback: "Notices") + internal static let notices = Strings.tr("Localizable", "views.about.credits.notices", fallback: "Notices") /// Credits - internal static let title = Strings.tr("Localizable", "views.advanced.credits.title", fallback: "Credits") + internal static let title = Strings.tr("Localizable", "views.about.credits.title", fallback: "Credits") /// Translations - internal static let translations = Strings.tr("Localizable", "views.advanced.credits.translations", fallback: "Translations") + internal static let translations = Strings.tr("Localizable", "views.about.credits.translations", fallback: "Translations") } internal enum Links { /// Links - internal static let title = Strings.tr("Localizable", "views.advanced.links.title", fallback: "Links") + internal static let title = Strings.tr("Localizable", "views.about.links.title", fallback: "Links") internal enum Rows { /// Disclaimer - internal static let disclaimer = Strings.tr("Localizable", "views.advanced.links.rows.disclaimer", fallback: "Disclaimer") + internal static let disclaimer = Strings.tr("Localizable", "views.about.links.rows.disclaimer", fallback: "Disclaimer") /// Home page - internal static let homePage = Strings.tr("Localizable", "views.advanced.links.rows.home_page", fallback: "Home page") + internal static let homePage = Strings.tr("Localizable", "views.about.links.rows.home_page", fallback: "Home page") /// Join community - internal static let joinCommunity = Strings.tr("Localizable", "views.advanced.links.rows.join_community", fallback: "Join community") + internal static let joinCommunity = Strings.tr("Localizable", "views.about.links.rows.join_community", fallback: "Join community") /// Privacy policy - internal static let privacyPolicy = Strings.tr("Localizable", "views.advanced.links.rows.privacy_policy", fallback: "Privacy policy") + internal static let privacyPolicy = Strings.tr("Localizable", "views.about.links.rows.privacy_policy", fallback: "Privacy policy") /// Write a review - internal static let writeReview = Strings.tr("Localizable", "views.advanced.links.rows.write_review", fallback: "Write a review") + internal static let writeReview = Strings.tr("Localizable", "views.about.links.rows.write_review", fallback: "Write a review") } internal enum Sections { /// Support - internal static let support = Strings.tr("Localizable", "views.advanced.links.sections.support", fallback: "Support") + internal static let support = Strings.tr("Localizable", "views.about.links.sections.support", fallback: "Support") /// Web - internal static let web = Strings.tr("Localizable", "views.advanced.links.sections.web", fallback: "Web") + internal static let web = Strings.tr("Localizable", "views.about.links.sections.web", fallback: "Web") } } internal enum Sections { /// Resources - internal static let resources = Strings.tr("Localizable", "views.advanced.sections.resources", fallback: "Resources") + internal static let resources = Strings.tr("Localizable", "views.about.sections.resources", fallback: "Resources") } } internal enum Diagnostics { @@ -479,10 +479,6 @@ internal enum Strings { /// Make a donation internal static let title = Strings.tr("Localizable", "views.donate.title", fallback: "Make a donation") } - internal enum Lockable { - /// Passepartout is locked - internal static let message = Strings.tr("Localizable", "views.lockable.message", fallback: "Passepartout is locked") - } internal enum Profile { internal enum ModuleList { internal enum Section { @@ -543,6 +539,20 @@ internal enum Strings { internal static let newProfile = Strings.tr("Localizable", "views.profiles.toolbar.new_profile", fallback: "New profile") } } + internal enum Settings { + internal enum Rows { + /// Lock in background + internal static let lockInBackground = Strings.tr("Localizable", "views.settings.rows.lock_in_background", fallback: "Lock in background") + internal enum LockInBackground { + /// Passepartout is locked + internal static let message = Strings.tr("Localizable", "views.settings.rows.lock_in_background.message", fallback: "Passepartout is locked") + } + } + internal enum Sections { + /// Lock + internal static let lock = Strings.tr("Localizable", "views.settings.sections.lock", fallback: "Lock") + } + } } } // swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length diff --git a/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings b/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings index 0df659ed..9d8695d5 100644 --- a/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings +++ b/Passepartout/Library/Sources/AppUI/Resources/en.lproj/Localizable.strings @@ -1,5 +1,6 @@ // MARK: Global +"global.about" = "About"; "global.account" = "Account"; "global.address" = "Address"; "global.addresses" = "Addresses"; @@ -119,23 +120,26 @@ "views.profile.rows.add_module" = "Add module"; "views.profile.module_list.section.footer" = "Drag modules to rearrange them, as their order determines priority."; -"views.advanced.title" = "Advanced"; -"views.advanced.sections.resources" = "Resources"; -"views.advanced.lock_in_background" = "Lock app access"; +"views.settings.sections.lock" = "Lock"; +"views.settings.rows.lock_in_background" = "Lock in background"; +"views.settings.rows.lock_in_background.message" = "Passepartout is locked"; -"views.advanced.links.title" = "Links"; -"views.advanced.links.sections.support" = "Support"; -"views.advanced.links.sections.web" = "Web"; -"views.advanced.links.rows.join_community" = "Join community"; -"views.advanced.links.rows.write_review" = "Write a review"; -"views.advanced.links.rows.home_page" = "Home page"; -"views.advanced.links.rows.disclaimer" = "Disclaimer"; -"views.advanced.links.rows.privacy_policy" = "Privacy policy"; +"views.about.title" = "About"; +"views.about.sections.resources" = "Resources"; -"views.advanced.credits.title" = "Credits"; -"views.advanced.credits.licenses" = "Licenses"; -"views.advanced.credits.notices" = "Notices"; -"views.advanced.credits.translations" = "Translations"; +"views.about.links.title" = "Links"; +"views.about.links.sections.support" = "Support"; +"views.about.links.sections.web" = "Web"; +"views.about.links.rows.join_community" = "Join community"; +"views.about.links.rows.write_review" = "Write a review"; +"views.about.links.rows.home_page" = "Home page"; +"views.about.links.rows.disclaimer" = "Disclaimer"; +"views.about.links.rows.privacy_policy" = "Privacy policy"; + +"views.about.credits.title" = "Credits"; +"views.about.credits.licenses" = "Licenses"; +"views.about.credits.notices" = "Notices"; +"views.about.credits.translations" = "Translations"; "views.donate.title" = "Make a donation"; @@ -152,8 +156,6 @@ "views.diagnostics.report_issue.title" = "Report issue"; "views.diagnostics.alerts.report_issue.email" = "The device is not configured to send e-mails."; -"views.lockable.message" = "Passepartout is locked"; - // MARK: - Module views "modules.dns.servers.add" = "Add address"; diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/AdvancedRouterView.swift b/Passepartout/Library/Sources/AppUI/Views/About/AboutRouterView.swift similarity index 95% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/AdvancedRouterView.swift rename to Passepartout/Library/Sources/AppUI/Views/About/AboutRouterView.swift index a5e885da..cb93de36 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/AdvancedRouterView.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/AboutRouterView.swift @@ -1,5 +1,5 @@ // -// AdvancedRouterView.swift +// AboutRouterView.swift // Passepartout // // Created by Davide De Rosa on 8/22/24. @@ -28,7 +28,7 @@ import CommonLibrary import PassepartoutKit import SwiftUI -struct AdvancedRouterView: View { +struct AboutRouterView: View { @Environment(\.dismiss) var dismiss @@ -43,7 +43,7 @@ struct AdvancedRouterView: View { } } -extension AdvancedRouterView { +extension AboutRouterView { enum NavigationRoute: Hashable { case donate @@ -94,7 +94,7 @@ extension AdvancedRouterView { } #Preview { - AdvancedRouterView( + AboutRouterView( tunnel: .mock ) .environmentObject(Theme()) diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/AdvancedView.swift b/Passepartout/Library/Sources/AppUI/Views/About/AboutView.swift similarity index 68% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/AdvancedView.swift rename to Passepartout/Library/Sources/AppUI/Views/About/AboutView.swift index 31293395..834c002a 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/AdvancedView.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/AboutView.swift @@ -1,5 +1,5 @@ // -// AdvancedView.swift +// AboutView.swift // Passepartout // // Created by Davide De Rosa on 8/23/24. @@ -28,27 +28,19 @@ import PassepartoutKit import SwiftUI import UtilsLibrary -struct AdvancedView: View { - - @AppStorage(AppPreference.locksInBackground.key) - private var locksInBackground = false - +struct AboutView: View { let identifiers: Constants.Identifiers @Binding - var navigationRoute: AdvancedRouterView.NavigationRoute? + var navigationRoute: AboutRouterView.NavigationRoute? var body: some View { listView - .navigationTitle(Strings.Views.Advanced.title) + .navigationTitle(Strings.Views.About.title) } } -extension AdvancedView { - var lockInBackgroundToggle: some View { - Toggle(Strings.Views.Advanced.lockInBackground, isOn: $locksInBackground) - } - +extension AboutView { var donateLink: some View { navLink(Strings.Views.Donate.title, to: .donate) } @@ -58,22 +50,22 @@ extension AdvancedView { } var linksLink: some View { - navLink(Strings.Views.Advanced.Links.title, to: .links) + navLink(Strings.Views.About.Links.title, to: .links) } var creditsLink: some View { - navLink(Strings.Views.Advanced.Credits.title, to: .credits) + navLink(Strings.Views.About.Credits.title, to: .credits) } } -private extension AdvancedView { - func navLink(_ title: String, to route: AdvancedRouterView.NavigationRoute) -> some View { +private extension AboutView { + func navLink(_ title: String, to route: AboutRouterView.NavigationRoute) -> some View { NavigationLink(title, value: route) } } #Preview { - AdvancedView( + AboutView( identifiers: Constants.shared.identifiers, navigationRoute: .constant(nil) ) diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/CreditsView.swift b/Passepartout/Library/Sources/AppUI/Views/About/CreditsView.swift similarity index 82% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/CreditsView.swift rename to Passepartout/Library/Sources/AppUI/Views/About/CreditsView.swift index dc3c954e..45244475 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/CreditsView.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/CreditsView.swift @@ -30,15 +30,15 @@ struct CreditsView: View { var body: some View { GenericCreditsView( credits: Self.credits, - licensesHeader: Strings.Views.Advanced.Credits.licenses, - noticesHeader: Strings.Views.Advanced.Credits.notices, - translationsHeader: Strings.Views.Advanced.Credits.translations, + licensesHeader: Strings.Views.About.Credits.licenses, + noticesHeader: Strings.Views.About.Credits.notices, + translationsHeader: Strings.Views.About.Credits.translations, errorDescription: { AppError($0) .localizedDescription } ) - .navigationTitle(Strings.Views.Advanced.Credits.title) + .navigationTitle(Strings.Views.About.Credits.title) .themeForm() } } diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/DonateView.swift b/Passepartout/Library/Sources/AppUI/Views/About/DonateView.swift similarity index 100% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/DonateView.swift rename to Passepartout/Library/Sources/AppUI/Views/About/DonateView.swift diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/LinksView.swift b/Passepartout/Library/Sources/AppUI/Views/About/LinksView.swift similarity index 67% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/LinksView.swift rename to Passepartout/Library/Sources/AppUI/Views/About/LinksView.swift index 120edf99..3f3a0980 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/LinksView.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/LinksView.swift @@ -32,7 +32,7 @@ struct LinksView: View { supportSection webSection } - .navigationTitle(Strings.Views.Advanced.Links.title) + .navigationTitle(Strings.Views.About.Links.title) .themeForm() } } @@ -44,21 +44,21 @@ private extension LinksView { var supportSection: some View { Section { - Link(Strings.Views.Advanced.Links.Rows.joinCommunity, destination: constants.websites.subreddit) - Link(Strings.Views.Advanced.Links.Rows.writeReview, destination: constants.urlForReview) + Link(Strings.Views.About.Links.Rows.joinCommunity, destination: constants.websites.subreddit) + Link(Strings.Views.About.Links.Rows.writeReview, destination: constants.urlForReview) } header: { - Text(Strings.Views.Advanced.Links.Sections.support) + Text(Strings.Views.About.Links.Sections.support) } } var webSection: some View { Section { - Link(Strings.Views.Advanced.Links.Rows.homePage, destination: constants.websites.home) + Link(Strings.Views.About.Links.Rows.homePage, destination: constants.websites.home) Link(Strings.Unlocalized.faq, destination: constants.websites.faq) - Link(Strings.Views.Advanced.Links.Rows.disclaimer, destination: constants.websites.disclaimer) - Link(Strings.Views.Advanced.Links.Rows.privacyPolicy, destination: constants.websites.privacyPolicy) + Link(Strings.Views.About.Links.Rows.disclaimer, destination: constants.websites.disclaimer) + Link(Strings.Views.About.Links.Rows.privacyPolicy, destination: constants.websites.privacyPolicy) } header: { - Text(Strings.Views.Advanced.Links.Sections.web) + Text(Strings.Views.About.Links.Sections.web) } } } diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/iOS/AdvancedRouterView+iOS.swift b/Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutRouterView+iOS.swift similarity index 94% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/iOS/AdvancedRouterView+iOS.swift rename to Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutRouterView+iOS.swift index 0ba4a763..413ef044 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/iOS/AdvancedRouterView+iOS.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutRouterView+iOS.swift @@ -1,5 +1,5 @@ // -// AdvancedRouterView+iOS.swift +// AboutRouterView+iOS.swift // Passepartout // // Created by Davide De Rosa on 8/26/24. @@ -28,10 +28,10 @@ import CommonLibrary import SwiftUI -extension AdvancedRouterView { +extension AboutRouterView { var body: some View { NavigationStack { - AdvancedView( + AboutView( identifiers: Constants.shared.identifiers, navigationRoute: $navigationRoute ) diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/iOS/AdvancedView+iOS.swift b/Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutView+iOS.swift similarity index 83% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/iOS/AdvancedView+iOS.swift rename to Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutView+iOS.swift index 99006022..e9a33a0e 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/iOS/AdvancedView+iOS.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/iOS/AboutView+iOS.swift @@ -1,5 +1,5 @@ // -// AdvancedView+iOS.swift +// AboutView+iOS.swift // Passepartout // // Created by Davide De Rosa on 8/27/24. @@ -27,21 +27,16 @@ import SwiftUI -extension AdvancedView { +extension AboutView { var listView: some View { List { - Section { - lockInBackgroundToggle - } header: { - Text(Strings.Global.settings) - } Section { // TODO: donations // donateLink linksLink creditsLink } header: { - Text(Strings.Views.Advanced.Sections.resources) + Text(Strings.Views.About.Sections.resources) } Section { diagnosticsLink diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/macOS/AdvancedRouterView+macOS.swift b/Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutRouterView+macOS.swift similarity index 94% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/macOS/AdvancedRouterView+macOS.swift rename to Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutRouterView+macOS.swift index fdfdcf00..67f1bad8 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/macOS/AdvancedRouterView+macOS.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutRouterView+macOS.swift @@ -1,5 +1,5 @@ // -// AdvancedRouterView+macOS.swift +// AboutRouterView+macOS.swift // Passepartout // // Created by Davide De Rosa on 8/26/24. @@ -28,10 +28,10 @@ import CommonLibrary import SwiftUI -extension AdvancedRouterView { +extension AboutRouterView { var body: some View { NavigationSplitView { - AdvancedView( + AboutView( identifiers: Constants.shared.identifiers, navigationRoute: $navigationRoute ) diff --git a/Passepartout/Library/Sources/AppUI/Views/Advanced/macOS/AdvancedView+macOS.swift b/Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutView+macOS.swift similarity index 86% rename from Passepartout/Library/Sources/AppUI/Views/Advanced/macOS/AdvancedView+macOS.swift rename to Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutView+macOS.swift index 56ea5f16..d27f2ca6 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Advanced/macOS/AdvancedView+macOS.swift +++ b/Passepartout/Library/Sources/AppUI/Views/About/macOS/AboutView+macOS.swift @@ -1,5 +1,5 @@ // -// AdvancedView+macOS.swift +// AboutView+macOS.swift // Passepartout // // Created by Davide De Rosa on 8/27/24. @@ -27,14 +27,9 @@ import SwiftUI #if os(macOS) -extension AdvancedView { +extension AboutView { var listView: some View { List(selection: $navigationRoute) { - Section { - lockInBackgroundToggle - } header: { - Text(Strings.Global.settings) - } Section { // TODO: donations // donateLink diff --git a/Passepartout/Library/Sources/AppUI/Views/App/AppInlineCoordinator.swift b/Passepartout/Library/Sources/AppUI/Views/App/AppInlineCoordinator.swift index 60eb45b0..ec1386c3 100644 --- a/Passepartout/Library/Sources/AppUI/Views/App/AppInlineCoordinator.swift +++ b/Passepartout/Library/Sources/AppUI/Views/App/AppInlineCoordinator.swift @@ -71,11 +71,10 @@ private extension AppInlineCoordinator { enum ModalRoute: String, Identifiable { case settings - var id: [String] { - switch self { - case .settings: - return ["settings"] - } + case about + + var id: String { + rawValue } } @@ -103,6 +102,9 @@ private extension AppInlineCoordinator { onSettings: { modalRoute = .settings }, + onAbout: { + modalRoute = .about + }, onNewProfile: enterDetail ) } @@ -127,7 +129,10 @@ private extension AppInlineCoordinator { func modalDestination(for item: ModalRoute?) -> some View { switch item { case .settings: - AdvancedRouterView(tunnel: tunnel) + SettingsView() + + case .about: + AboutRouterView(tunnel: tunnel) default: EmptyView() diff --git a/Passepartout/Library/Sources/AppUI/Views/App/AppModalCoordinator.swift b/Passepartout/Library/Sources/AppUI/Views/App/AppModalCoordinator.swift index a1f866a5..d10b689e 100644 --- a/Passepartout/Library/Sources/AppUI/Views/App/AppModalCoordinator.swift +++ b/Passepartout/Library/Sources/AppUI/Views/App/AppModalCoordinator.swift @@ -62,19 +62,15 @@ struct AppModalCoordinator: View { // MARK: - Destinations extension AppModalCoordinator { - enum ModalRoute: Identifiable { + enum ModalRoute: String, Identifiable { case editProfile case settings - var id: [String] { - switch self { - case .editProfile: - return ["editProfile"] + case about - case .settings: - return ["settings"] - } + var id: String { + rawValue } } @@ -102,6 +98,9 @@ extension AppModalCoordinator { onSettings: { modalRoute = .settings }, + onAbout: { + modalRoute = .about + }, onNewProfile: enterDetail ) } @@ -121,7 +120,10 @@ extension AppModalCoordinator { } case .settings: - AdvancedRouterView(tunnel: tunnel) + SettingsView() + + case .about: + AboutRouterView(tunnel: tunnel) default: EmptyView() diff --git a/Passepartout/Library/Sources/AppUI/Views/App/AppToolbar.swift b/Passepartout/Library/Sources/AppUI/Views/App/AppToolbar.swift index 0ec9717f..3e1f02b2 100644 --- a/Passepartout/Library/Sources/AppUI/Views/App/AppToolbar.swift +++ b/Passepartout/Library/Sources/AppUI/Views/App/AppToolbar.swift @@ -45,18 +45,20 @@ struct AppToolbar: ToolbarContent { let onSettings: () -> Void + let onAbout: () -> Void + let onNewProfile: (Profile) -> Void var body: some ToolbarContent { if hsClass == .regular && vsClass == .regular { ToolbarItemGroup { addProfileMenu - settingsButton + aboutButton layoutPicker } } else { ToolbarItem(placement: .navigation) { - settingsButton + moreMenu } ToolbarItemGroup(placement: .primaryAction) { addProfileMenu @@ -75,9 +77,24 @@ private extension AppToolbar { ) } + var moreMenu: some View { + Menu { + settingsButton + aboutButton + } label: { + ThemeImage(.moreDetails) + } + } + var settingsButton: some View { Button(action: onSettings) { - ThemeImage(.advanced) + ThemeImageLabel(Strings.Global.settings, .settings) + } + } + + var aboutButton: some View { + Button(action: onAbout) { + ThemeImageLabel(Strings.Global.about, .info) } } @@ -95,6 +112,7 @@ private extension AppToolbar { layout: .constant(.list), isImporting: .constant(false), onSettings: {}, + onAbout: {}, onNewProfile: { _ in} ) } diff --git a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/DiagnosticsView.swift b/Passepartout/Library/Sources/AppUI/Views/Diagnostics/DiagnosticsView.swift index d30f2aee..5fc2051e 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Diagnostics/DiagnosticsView.swift +++ b/Passepartout/Library/Sources/AppUI/Views/Diagnostics/DiagnosticsView.swift @@ -161,7 +161,7 @@ private extension DiagnosticsView { } } - func navLink(_ title: String, to value: AdvancedRouterView.NavigationRoute) -> some View { + func navLink(_ title: String, to value: AboutRouterView.NavigationRoute) -> some View { NavigationLink(title, value: value) } } diff --git a/Passepartout/Library/Sources/AppUI/Views/Settings/SettingsView.swift b/Passepartout/Library/Sources/AppUI/Views/Settings/SettingsView.swift new file mode 100644 index 00000000..a2dc31b7 --- /dev/null +++ b/Passepartout/Library/Sources/AppUI/Views/Settings/SettingsView.swift @@ -0,0 +1,61 @@ +// +// SettingsView.swift +// Passepartout +// +// Created by Davide De Rosa on 9/28/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 . +// + +import CommonLibrary +import SwiftUI + +public struct SettingsView: View { + + @AppStorage(AppPreference.locksInBackground.key) + private var locksInBackground = false + + @State + private var path = NavigationPath() + + public init() { + } + + public var body: some View { + Form { + Section { + lockInBackgroundToggle + } header: { + Text(Strings.Views.Settings.Sections.lock) + } + } + .themeForm() + .navigationTitle(Strings.Global.settings) +#if os(iOS) + .themeNavigationDetail() + .themeNavigationStack(if: true, closable: true, path: $path) +#endif + } +} + +private extension SettingsView { + var lockInBackgroundToggle: some View { + Toggle(Strings.Views.Settings.Rows.lockInBackground, isOn: $locksInBackground) + } +} diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+ImageName.swift b/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+ImageName.swift index b575029b..153b42b6 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+ImageName.swift +++ b/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+ImageName.swift @@ -28,7 +28,6 @@ import Foundation extension Theme { public enum ImageName { case add - case advanced case close case contextDuplicate case contextRemove @@ -47,6 +46,7 @@ extension Theme { case profilesGrid case profilesList case remove + case settings case share case show case sleeping diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+UI.swift b/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+UI.swift index fc9e66bb..c253bcd7 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+UI.swift +++ b/Passepartout/Library/Sources/AppUI/Views/Theme/Theme+UI.swift @@ -102,6 +102,40 @@ struct ThemeItemModalModifier: ViewModifier where Modal: View, T: Iden } } +struct ThemeNavigationStackModifier: ViewModifier { + + @Environment(\.dismiss) + private var dismiss + + let condition: Bool + + let closable: Bool + + @Binding + var path: NavigationPath + + func body(content: Content) -> some View { + if condition { + NavigationStack(path: $path) { + content + .toolbar { + if closable { + ToolbarItem(placement: .cancellationAction) { + Button { + dismiss() + } label: { + ThemeImage(.close) + } + } + } + } + } + } else { + content + } + } +} + struct ThemePlainButtonModifier: ViewModifier { let action: () -> Void } @@ -228,7 +262,7 @@ struct ThemeLockScreenModifier: ViewModifier { do { let isAuthorized = try await context.evaluatePolicy( policy, - localizedReason: Strings.Views.Lockable.message + localizedReason: Strings.Views.Settings.Rows.LockInBackground.message ) return isAuthorized } catch { diff --git a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme.swift b/Passepartout/Library/Sources/AppUI/Views/Theme/Theme.swift index 2a4f395b..9f382dd4 100644 --- a/Passepartout/Library/Sources/AppUI/Views/Theme/Theme.swift +++ b/Passepartout/Library/Sources/AppUI/Views/Theme/Theme.swift @@ -79,7 +79,6 @@ public final class Theme: ObservableObject { var systemImage: (ImageName) -> String = { switch $0 { case .add: return "plus" - case .advanced: return "gearshape" case .close: return "xmark" case .contextDuplicate: return "plus.square.on.square" case .contextRemove: return "trash" @@ -98,6 +97,7 @@ public final class Theme: ObservableObject { case .profilesGrid: return "square.grid.2x2" case .profilesList: return "rectangle.grid.1x2" case .remove: return "minus" + case .settings: return "gearshape" case .share: return "square.and.arrow.up" case .show: return "eye" case .sleeping: return "powersleep" @@ -160,15 +160,8 @@ extension View { )) } - @ViewBuilder - public func themeNavigationStack(if condition: Bool, path: Binding) -> some View { - if condition { - NavigationStack(path: path) { - self - } - } else { - self - } + public func themeNavigationStack(if condition: Bool, closable: Bool = false, path: Binding) -> some View { + modifier(ThemeNavigationStackModifier(condition: condition, closable: closable, path: path)) } public func themePlainButton(action: @escaping () -> Void) -> some View {