Prepare for macOS settings (#629)

This commit is contained in:
Davide 2024-09-28 12:47:33 +02:00 committed by GitHub
parent c0234b07a8
commit 5deb8ec763
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 238 additions and 139 deletions

View File

@ -33,21 +33,8 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
#else #else
import AppKit import AppKit
import CommonLibrary
import PassepartoutKit
final class AppDelegate: NSObject, NSApplicationDelegate { 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 #endif

View File

@ -53,6 +53,11 @@ struct PassepartoutApp: App {
#else #else
Window(appName, id: appName, content: content) Window(appName, id: appName, content: content)
.defaultSize(width: 600.0, height: 400.0) .defaultSize(width: 600.0, height: 400.0)
// Settings {
// SettingsView()
// .frame(minWidth: 300, minHeight: 100)
// }
#endif #endif
} }
} }

View File

@ -139,6 +139,8 @@ internal enum Strings {
} }
} }
internal enum Global { internal enum Global {
/// About
internal static let about = Strings.tr("Localizable", "global.about", fallback: "About")
/// Account /// Account
internal static let account = Strings.tr("Localizable", "global.account", fallback: "Account") internal static let account = Strings.tr("Localizable", "global.account", fallback: "Account")
/// Address /// Address
@ -397,46 +399,44 @@ internal enum Strings {
} }
} }
internal enum Views { internal enum Views {
internal enum Advanced { internal enum About {
/// Lock app access /// About
internal static let lockInBackground = Strings.tr("Localizable", "views.advanced.lock_in_background", fallback: "Lock app access") internal static let title = Strings.tr("Localizable", "views.about.title", fallback: "About")
/// Advanced
internal static let title = Strings.tr("Localizable", "views.advanced.title", fallback: "Advanced")
internal enum Credits { internal enum Credits {
/// Licenses /// 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 /// 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 /// 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 /// 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 { internal enum Links {
/// 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 { internal enum Rows {
/// Disclaimer /// 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 /// 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 /// 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 /// 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 /// 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 { internal enum Sections {
/// Support /// 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 /// 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 { internal enum Sections {
/// Resources /// 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 { internal enum Diagnostics {
@ -479,10 +479,6 @@ internal enum Strings {
/// Make a donation /// Make a donation
internal static let title = Strings.tr("Localizable", "views.donate.title", fallback: "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 Profile {
internal enum ModuleList { internal enum ModuleList {
internal enum Section { 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 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 // swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length

View File

@ -1,5 +1,6 @@
// MARK: Global // MARK: Global
"global.about" = "About";
"global.account" = "Account"; "global.account" = "Account";
"global.address" = "Address"; "global.address" = "Address";
"global.addresses" = "Addresses"; "global.addresses" = "Addresses";
@ -119,23 +120,26 @@
"views.profile.rows.add_module" = "Add module"; "views.profile.rows.add_module" = "Add module";
"views.profile.module_list.section.footer" = "Drag modules to rearrange them, as their order determines priority."; "views.profile.module_list.section.footer" = "Drag modules to rearrange them, as their order determines priority.";
"views.advanced.title" = "Advanced"; "views.settings.sections.lock" = "Lock";
"views.advanced.sections.resources" = "Resources"; "views.settings.rows.lock_in_background" = "Lock in background";
"views.advanced.lock_in_background" = "Lock app access"; "views.settings.rows.lock_in_background.message" = "Passepartout is locked";
"views.advanced.links.title" = "Links"; "views.about.title" = "About";
"views.advanced.links.sections.support" = "Support"; "views.about.sections.resources" = "Resources";
"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.advanced.credits.title" = "Credits"; "views.about.links.title" = "Links";
"views.advanced.credits.licenses" = "Licenses"; "views.about.links.sections.support" = "Support";
"views.advanced.credits.notices" = "Notices"; "views.about.links.sections.web" = "Web";
"views.advanced.credits.translations" = "Translations"; "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"; "views.donate.title" = "Make a donation";
@ -152,8 +156,6 @@
"views.diagnostics.report_issue.title" = "Report issue"; "views.diagnostics.report_issue.title" = "Report issue";
"views.diagnostics.alerts.report_issue.email" = "The device is not configured to send e-mails."; "views.diagnostics.alerts.report_issue.email" = "The device is not configured to send e-mails.";
"views.lockable.message" = "Passepartout is locked";
// MARK: - Module views // MARK: - Module views
"modules.dns.servers.add" = "Add address"; "modules.dns.servers.add" = "Add address";

View File

@ -1,5 +1,5 @@
// //
// AdvancedRouterView.swift // AboutRouterView.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/22/24. // Created by Davide De Rosa on 8/22/24.
@ -28,7 +28,7 @@ import CommonLibrary
import PassepartoutKit import PassepartoutKit
import SwiftUI import SwiftUI
struct AdvancedRouterView: View { struct AboutRouterView: View {
@Environment(\.dismiss) @Environment(\.dismiss)
var dismiss var dismiss
@ -43,7 +43,7 @@ struct AdvancedRouterView: View {
} }
} }
extension AdvancedRouterView { extension AboutRouterView {
enum NavigationRoute: Hashable { enum NavigationRoute: Hashable {
case donate case donate
@ -94,7 +94,7 @@ extension AdvancedRouterView {
} }
#Preview { #Preview {
AdvancedRouterView( AboutRouterView(
tunnel: .mock tunnel: .mock
) )
.environmentObject(Theme()) .environmentObject(Theme())

View File

@ -1,5 +1,5 @@
// //
// AdvancedView.swift // AboutView.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/23/24. // Created by Davide De Rosa on 8/23/24.
@ -28,27 +28,19 @@ import PassepartoutKit
import SwiftUI import SwiftUI
import UtilsLibrary import UtilsLibrary
struct AdvancedView: View { struct AboutView: View {
@AppStorage(AppPreference.locksInBackground.key)
private var locksInBackground = false
let identifiers: Constants.Identifiers let identifiers: Constants.Identifiers
@Binding @Binding
var navigationRoute: AdvancedRouterView.NavigationRoute? var navigationRoute: AboutRouterView.NavigationRoute?
var body: some View { var body: some View {
listView listView
.navigationTitle(Strings.Views.Advanced.title) .navigationTitle(Strings.Views.About.title)
} }
} }
extension AdvancedView { extension AboutView {
var lockInBackgroundToggle: some View {
Toggle(Strings.Views.Advanced.lockInBackground, isOn: $locksInBackground)
}
var donateLink: some View { var donateLink: some View {
navLink(Strings.Views.Donate.title, to: .donate) navLink(Strings.Views.Donate.title, to: .donate)
} }
@ -58,22 +50,22 @@ extension AdvancedView {
} }
var linksLink: some View { var linksLink: some View {
navLink(Strings.Views.Advanced.Links.title, to: .links) navLink(Strings.Views.About.Links.title, to: .links)
} }
var creditsLink: some View { var creditsLink: some View {
navLink(Strings.Views.Advanced.Credits.title, to: .credits) navLink(Strings.Views.About.Credits.title, to: .credits)
} }
} }
private extension AdvancedView { private extension AboutView {
func navLink(_ title: String, to route: AdvancedRouterView.NavigationRoute) -> some View { func navLink(_ title: String, to route: AboutRouterView.NavigationRoute) -> some View {
NavigationLink(title, value: route) NavigationLink(title, value: route)
} }
} }
#Preview { #Preview {
AdvancedView( AboutView(
identifiers: Constants.shared.identifiers, identifiers: Constants.shared.identifiers,
navigationRoute: .constant(nil) navigationRoute: .constant(nil)
) )

View File

@ -30,15 +30,15 @@ struct CreditsView: View {
var body: some View { var body: some View {
GenericCreditsView( GenericCreditsView(
credits: Self.credits, credits: Self.credits,
licensesHeader: Strings.Views.Advanced.Credits.licenses, licensesHeader: Strings.Views.About.Credits.licenses,
noticesHeader: Strings.Views.Advanced.Credits.notices, noticesHeader: Strings.Views.About.Credits.notices,
translationsHeader: Strings.Views.Advanced.Credits.translations, translationsHeader: Strings.Views.About.Credits.translations,
errorDescription: { errorDescription: {
AppError($0) AppError($0)
.localizedDescription .localizedDescription
} }
) )
.navigationTitle(Strings.Views.Advanced.Credits.title) .navigationTitle(Strings.Views.About.Credits.title)
.themeForm() .themeForm()
} }
} }

View File

@ -32,7 +32,7 @@ struct LinksView: View {
supportSection supportSection
webSection webSection
} }
.navigationTitle(Strings.Views.Advanced.Links.title) .navigationTitle(Strings.Views.About.Links.title)
.themeForm() .themeForm()
} }
} }
@ -44,21 +44,21 @@ private extension LinksView {
var supportSection: some View { var supportSection: some View {
Section { Section {
Link(Strings.Views.Advanced.Links.Rows.joinCommunity, destination: constants.websites.subreddit) Link(Strings.Views.About.Links.Rows.joinCommunity, destination: constants.websites.subreddit)
Link(Strings.Views.Advanced.Links.Rows.writeReview, destination: constants.urlForReview) Link(Strings.Views.About.Links.Rows.writeReview, destination: constants.urlForReview)
} header: { } header: {
Text(Strings.Views.Advanced.Links.Sections.support) Text(Strings.Views.About.Links.Sections.support)
} }
} }
var webSection: some View { var webSection: some View {
Section { 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.Unlocalized.faq, destination: constants.websites.faq)
Link(Strings.Views.Advanced.Links.Rows.disclaimer, destination: constants.websites.disclaimer) Link(Strings.Views.About.Links.Rows.disclaimer, destination: constants.websites.disclaimer)
Link(Strings.Views.Advanced.Links.Rows.privacyPolicy, destination: constants.websites.privacyPolicy) Link(Strings.Views.About.Links.Rows.privacyPolicy, destination: constants.websites.privacyPolicy)
} header: { } header: {
Text(Strings.Views.Advanced.Links.Sections.web) Text(Strings.Views.About.Links.Sections.web)
} }
} }
} }

View File

@ -1,5 +1,5 @@
// //
// AdvancedRouterView+iOS.swift // AboutRouterView+iOS.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/26/24. // Created by Davide De Rosa on 8/26/24.
@ -28,10 +28,10 @@
import CommonLibrary import CommonLibrary
import SwiftUI import SwiftUI
extension AdvancedRouterView { extension AboutRouterView {
var body: some View { var body: some View {
NavigationStack { NavigationStack {
AdvancedView( AboutView(
identifiers: Constants.shared.identifiers, identifiers: Constants.shared.identifiers,
navigationRoute: $navigationRoute navigationRoute: $navigationRoute
) )

View File

@ -1,5 +1,5 @@
// //
// AdvancedView+iOS.swift // AboutView+iOS.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/27/24. // Created by Davide De Rosa on 8/27/24.
@ -27,21 +27,16 @@
import SwiftUI import SwiftUI
extension AdvancedView { extension AboutView {
var listView: some View { var listView: some View {
List { List {
Section {
lockInBackgroundToggle
} header: {
Text(Strings.Global.settings)
}
Section { Section {
// TODO: donations // TODO: donations
// donateLink // donateLink
linksLink linksLink
creditsLink creditsLink
} header: { } header: {
Text(Strings.Views.Advanced.Sections.resources) Text(Strings.Views.About.Sections.resources)
} }
Section { Section {
diagnosticsLink diagnosticsLink

View File

@ -1,5 +1,5 @@
// //
// AdvancedRouterView+macOS.swift // AboutRouterView+macOS.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/26/24. // Created by Davide De Rosa on 8/26/24.
@ -28,10 +28,10 @@
import CommonLibrary import CommonLibrary
import SwiftUI import SwiftUI
extension AdvancedRouterView { extension AboutRouterView {
var body: some View { var body: some View {
NavigationSplitView { NavigationSplitView {
AdvancedView( AboutView(
identifiers: Constants.shared.identifiers, identifiers: Constants.shared.identifiers,
navigationRoute: $navigationRoute navigationRoute: $navigationRoute
) )

View File

@ -1,5 +1,5 @@
// //
// AdvancedView+macOS.swift // AboutView+macOS.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 8/27/24. // Created by Davide De Rosa on 8/27/24.
@ -27,14 +27,9 @@ import SwiftUI
#if os(macOS) #if os(macOS)
extension AdvancedView { extension AboutView {
var listView: some View { var listView: some View {
List(selection: $navigationRoute) { List(selection: $navigationRoute) {
Section {
lockInBackgroundToggle
} header: {
Text(Strings.Global.settings)
}
Section { Section {
// TODO: donations // TODO: donations
// donateLink // donateLink

View File

@ -71,11 +71,10 @@ private extension AppInlineCoordinator {
enum ModalRoute: String, Identifiable { enum ModalRoute: String, Identifiable {
case settings case settings
var id: [String] { case about
switch self {
case .settings: var id: String {
return ["settings"] rawValue
}
} }
} }
@ -103,6 +102,9 @@ private extension AppInlineCoordinator {
onSettings: { onSettings: {
modalRoute = .settings modalRoute = .settings
}, },
onAbout: {
modalRoute = .about
},
onNewProfile: enterDetail onNewProfile: enterDetail
) )
} }
@ -127,7 +129,10 @@ private extension AppInlineCoordinator {
func modalDestination(for item: ModalRoute?) -> some View { func modalDestination(for item: ModalRoute?) -> some View {
switch item { switch item {
case .settings: case .settings:
AdvancedRouterView(tunnel: tunnel) SettingsView()
case .about:
AboutRouterView(tunnel: tunnel)
default: default:
EmptyView() EmptyView()

View File

@ -62,19 +62,15 @@ struct AppModalCoordinator: View {
// MARK: - Destinations // MARK: - Destinations
extension AppModalCoordinator { extension AppModalCoordinator {
enum ModalRoute: Identifiable { enum ModalRoute: String, Identifiable {
case editProfile case editProfile
case settings case settings
var id: [String] { case about
switch self {
case .editProfile:
return ["editProfile"]
case .settings: var id: String {
return ["settings"] rawValue
}
} }
} }
@ -102,6 +98,9 @@ extension AppModalCoordinator {
onSettings: { onSettings: {
modalRoute = .settings modalRoute = .settings
}, },
onAbout: {
modalRoute = .about
},
onNewProfile: enterDetail onNewProfile: enterDetail
) )
} }
@ -121,7 +120,10 @@ extension AppModalCoordinator {
} }
case .settings: case .settings:
AdvancedRouterView(tunnel: tunnel) SettingsView()
case .about:
AboutRouterView(tunnel: tunnel)
default: default:
EmptyView() EmptyView()

View File

@ -45,18 +45,20 @@ struct AppToolbar: ToolbarContent {
let onSettings: () -> Void let onSettings: () -> Void
let onAbout: () -> Void
let onNewProfile: (Profile) -> Void let onNewProfile: (Profile) -> Void
var body: some ToolbarContent { var body: some ToolbarContent {
if hsClass == .regular && vsClass == .regular { if hsClass == .regular && vsClass == .regular {
ToolbarItemGroup { ToolbarItemGroup {
addProfileMenu addProfileMenu
settingsButton aboutButton
layoutPicker layoutPicker
} }
} else { } else {
ToolbarItem(placement: .navigation) { ToolbarItem(placement: .navigation) {
settingsButton moreMenu
} }
ToolbarItemGroup(placement: .primaryAction) { ToolbarItemGroup(placement: .primaryAction) {
addProfileMenu addProfileMenu
@ -75,9 +77,24 @@ private extension AppToolbar {
) )
} }
var moreMenu: some View {
Menu {
settingsButton
aboutButton
} label: {
ThemeImage(.moreDetails)
}
}
var settingsButton: some View { var settingsButton: some View {
Button(action: onSettings) { 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), layout: .constant(.list),
isImporting: .constant(false), isImporting: .constant(false),
onSettings: {}, onSettings: {},
onAbout: {},
onNewProfile: { _ in} onNewProfile: { _ in}
) )
} }

View File

@ -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) NavigationLink(title, value: value)
} }
} }

View File

@ -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 <http://www.gnu.org/licenses/>.
//
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)
}
}

View File

@ -28,7 +28,6 @@ import Foundation
extension Theme { extension Theme {
public enum ImageName { public enum ImageName {
case add case add
case advanced
case close case close
case contextDuplicate case contextDuplicate
case contextRemove case contextRemove
@ -47,6 +46,7 @@ extension Theme {
case profilesGrid case profilesGrid
case profilesList case profilesList
case remove case remove
case settings
case share case share
case show case show
case sleeping case sleeping

View File

@ -102,6 +102,40 @@ struct ThemeItemModalModifier<Modal, T>: 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 { struct ThemePlainButtonModifier: ViewModifier {
let action: () -> Void let action: () -> Void
} }
@ -228,7 +262,7 @@ struct ThemeLockScreenModifier: ViewModifier {
do { do {
let isAuthorized = try await context.evaluatePolicy( let isAuthorized = try await context.evaluatePolicy(
policy, policy,
localizedReason: Strings.Views.Lockable.message localizedReason: Strings.Views.Settings.Rows.LockInBackground.message
) )
return isAuthorized return isAuthorized
} catch { } catch {

View File

@ -79,7 +79,6 @@ public final class Theme: ObservableObject {
var systemImage: (ImageName) -> String = { var systemImage: (ImageName) -> String = {
switch $0 { switch $0 {
case .add: return "plus" case .add: return "plus"
case .advanced: return "gearshape"
case .close: return "xmark" case .close: return "xmark"
case .contextDuplicate: return "plus.square.on.square" case .contextDuplicate: return "plus.square.on.square"
case .contextRemove: return "trash" case .contextRemove: return "trash"
@ -98,6 +97,7 @@ public final class Theme: ObservableObject {
case .profilesGrid: return "square.grid.2x2" case .profilesGrid: return "square.grid.2x2"
case .profilesList: return "rectangle.grid.1x2" case .profilesList: return "rectangle.grid.1x2"
case .remove: return "minus" case .remove: return "minus"
case .settings: return "gearshape"
case .share: return "square.and.arrow.up" case .share: return "square.and.arrow.up"
case .show: return "eye" case .show: return "eye"
case .sleeping: return "powersleep" case .sleeping: return "powersleep"
@ -160,15 +160,8 @@ extension View {
)) ))
} }
@ViewBuilder public func themeNavigationStack(if condition: Bool, closable: Bool = false, path: Binding<NavigationPath>) -> some View {
public func themeNavigationStack(if condition: Bool, path: Binding<NavigationPath>) -> some View { modifier(ThemeNavigationStackModifier(condition: condition, closable: closable, path: path))
if condition {
NavigationStack(path: path) {
self
}
} else {
self
}
} }
public func themePlainButton(action: @escaping () -> Void) -> some View { public func themePlainButton(action: @escaping () -> Void) -> some View {