diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Modules/OnDemandView.swift b/Passepartout/Library/Sources/AppUIMain/Views/Modules/OnDemandView.swift index 7bae92b0..3a073763 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Modules/OnDemandView.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Modules/OnDemandView.swift @@ -103,8 +103,7 @@ private extension OnDemandView { Text($0.localizedDescription) } } - .themeSection(footer: policyFooterDescription) - .themeRow(footer: policyFooterDescription) + .themeSectionWithSingleRow(footer: policyFooterDescription) } var policyFooterDescription: String { diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Profile/AppleTVSection.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/AppleTVSection.swift index 4c801ca7..8fee36b9 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Profile/AppleTVSection.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Profile/AppleTVSection.swift @@ -41,6 +41,7 @@ struct AppleTVSection: View { debugChanges() return Group { availableToggle + .themeRow(footer: footer) purchaseButton } .themeSection(footer: footer) @@ -51,7 +52,6 @@ struct AppleTVSection: View { private extension AppleTVSection { var availableToggle: some View { Toggle(Strings.Modules.General.Rows.appleTv(Strings.Unlocalized.appleTV), isOn: $profileEditor.isAvailableForTV) - .themeRow(footer: footer) } var purchaseButton: some View { diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Profile/StorageSection.swift b/Passepartout/Library/Sources/AppUIMain/Views/Profile/StorageSection.swift index fb783a5f..cf0884a2 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Profile/StorageSection.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Profile/StorageSection.swift @@ -39,24 +39,17 @@ struct StorageSection: View { var body: some View { debugChanges() - return Group { - sharingToggle - } - .themeSection( - header: Strings.Global.storage, - footer: footer - ) + return sharingToggle + .themeSectionWithSingleRow( + header: Strings.Global.storage, + footer: Strings.Modules.General.Sections.Storage.footer + ) } } private extension StorageSection { var sharingToggle: some View { Toggle(Strings.Modules.General.Rows.icloudSharing, isOn: $profileEditor.isShared) - .themeRow(footer: footer) - } - - var footer: String { - Strings.Modules.General.Sections.Storage.footer } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Provider/iOS/VPNProviderServerView+iOS.swift b/Passepartout/Library/Sources/AppUIMain/Views/Provider/iOS/VPNProviderServerView+iOS.swift index 58e7d8e4..c34655da 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Provider/iOS/VPNProviderServerView+iOS.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Provider/iOS/VPNProviderServerView+iOS.swift @@ -123,7 +123,7 @@ private extension VPNProviderServerView.ServersSubview { Section { Toggle(Strings.Providers.onlyFavorites, isOn: $filtersViewModel.onlyShowsFavorites) } - Section { + Group { if isFiltering || !servers.isEmpty { if isFiltering { ProgressView() @@ -134,9 +134,10 @@ private extension VPNProviderServerView.ServersSubview { } else { emptyView } - } header: { - Text(filtersViewModel.filters.categoryName ?? Strings.Providers.Vpn.Category.any) } + .themeSection( + header: filtersViewModel.filters.categoryName ?? Strings.Providers.Vpn.Category.any + ) } } diff --git a/Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsSectionGroup.swift b/Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsSectionGroup.swift index 88be554c..3d4e54c3 100644 --- a/Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsSectionGroup.swift +++ b/Passepartout/Library/Sources/AppUIMain/Views/Settings/SettingsSectionGroup.swift @@ -45,42 +45,34 @@ struct SettingsSectionGroup: View { private var isErasingiCloud = false var body: some View { - Group { -#if os(macOS) - keepsInMenuToggle -#endif #if os(iOS) - lockInBackgroundToggle + lockInBackgroundToggle #endif - } - .themeSection(header: Strings.Global.general) - Group { - eraseCloudKitButton - .themeRow(footer: iCloudFooter) - } - .themeSection( - header: Strings.Unlocalized.iCloud, - footer: iCloudFooter - ) +#if os(macOS) + keepsInMenuToggle +#endif + eraseCloudKitButton } } private extension SettingsSectionGroup { var keepsInMenuToggle: some View { - Toggle(Strings.Views.Settings.Rows.keepsInMenu, isOn: $keepsInMenu) + Toggle(Strings.Views.Settings.keepsInMenu, isOn: $keepsInMenu) + .themeSectionWithSingleRow(footer: Strings.Views.Settings.KeepsInMenu.footer) } var lockInBackgroundToggle: some View { - Toggle(Strings.Views.Settings.Rows.locksInBackground, isOn: $locksInBackground) + Toggle(Strings.Views.Settings.locksInBackground, isOn: $locksInBackground) + .themeSectionWithSingleRow(footer: Strings.Views.Settings.LocksInBackground.footer) } var eraseCloudKitButton: some View { - Button(Strings.Views.Settings.Rows.eraseIcloud, role: .destructive) { + Button(Strings.Views.Settings.eraseIcloud, role: .destructive) { isConfirmingEraseiCloud = true } .themeConfirmation( isPresented: $isConfirmingEraseiCloud, - title: Strings.Views.Settings.Rows.eraseIcloud, + title: Strings.Views.Settings.eraseIcloud, isDestructive: true ) { isErasingiCloud = true @@ -94,10 +86,11 @@ private extension SettingsSectionGroup { isErasingiCloud = false } } + .themeSectionWithSingleRow( + header: Strings.Unlocalized.iCloud, + footer: Strings.Views.Settings.EraseIcloud.footer, + above: true + ) .disabled(isErasingiCloud) } - - var iCloudFooter: String { - Strings.Views.Settings.Sections.Icloud.footer - } } diff --git a/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift b/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift index b71f9cc3..662ca46a 100644 --- a/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift +++ b/Passepartout/Library/Sources/AppUITV/Views/Profile/ProfileListView.swift @@ -47,11 +47,10 @@ struct ProfileListView: View { var body: some View { List { - Section { + Group { ForEach(headers, id: \.id, content: toggleButton(for:)) - } header: { - Text(Strings.Views.Profiles.Folders.default) } + .themeSection(header: Strings.Views.Profiles.Folders.default) } .listStyle(.grouped) .scrollClipDisabled() diff --git a/Passepartout/Library/Sources/UILibrary/L10n/SwiftGen+Strings.swift b/Passepartout/Library/Sources/UILibrary/L10n/SwiftGen+Strings.swift index dac89df8..54323f2a 100644 --- a/Passepartout/Library/Sources/UILibrary/L10n/SwiftGen+Strings.swift +++ b/Passepartout/Library/Sources/UILibrary/L10n/SwiftGen+Strings.swift @@ -696,19 +696,23 @@ public enum Strings { } } public enum Settings { - public enum Rows { - /// Erase iCloud store - public static let eraseIcloud = Strings.tr("Localizable", "views.settings.rows.erase_icloud", fallback: "Erase iCloud store") - /// Keep in menu bar - public static let keepsInMenu = Strings.tr("Localizable", "views.settings.rows.keeps_in_menu", fallback: "Keep in menu bar") - /// Lock in background - public static let locksInBackground = Strings.tr("Localizable", "views.settings.rows.locks_in_background", fallback: "Lock in background") + /// Erase iCloud store + public static let eraseIcloud = Strings.tr("Localizable", "views.settings.erase_icloud", fallback: "Erase iCloud store") + /// Keep in menu bar + public static let keepsInMenu = Strings.tr("Localizable", "views.settings.keeps_in_menu", fallback: "Keep in menu bar") + /// Lock in background + public static let locksInBackground = Strings.tr("Localizable", "views.settings.locks_in_background", fallback: "Lock in background") + public enum EraseIcloud { + /// To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles. + public static let footer = Strings.tr("Localizable", "views.settings.erase_icloud.footer", fallback: "To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles.") } - public enum Sections { - public enum Icloud { - /// To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles. - public static let footer = Strings.tr("Localizable", "views.settings.sections.icloud.footer", fallback: "To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles.") - } + public enum KeepsInMenu { + /// Enable this to keep the app in the menu bar after closing it. + public static let footer = Strings.tr("Localizable", "views.settings.keeps_in_menu.footer", fallback: "Enable this to keep the app in the menu bar after closing it.") + } + public enum LocksInBackground { + /// Lock the app with FaceID when sent to the background. + public static let footer = Strings.tr("Localizable", "views.settings.locks_in_background.footer", fallback: "Lock the app with FaceID when sent to the background.") } } } diff --git a/Passepartout/Library/Sources/UILibrary/Resources/en.lproj/Localizable.strings b/Passepartout/Library/Sources/UILibrary/Resources/en.lproj/Localizable.strings index 298d875a..3bd55f14 100644 --- a/Passepartout/Library/Sources/UILibrary/Resources/en.lproj/Localizable.strings +++ b/Passepartout/Library/Sources/UILibrary/Resources/en.lproj/Localizable.strings @@ -130,10 +130,12 @@ "views.profile.rows.add_module" = "Add module"; "views.profile.module_list.section.footer" = "Drag modules to rearrange them, as their order determines priority."; -"views.settings.sections.icloud.footer" = "To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles."; -"views.settings.rows.keeps_in_menu" = "Keep in menu bar"; -"views.settings.rows.locks_in_background" = "Lock in background"; -"views.settings.rows.erase_icloud" = "Erase iCloud store"; +"views.settings.keeps_in_menu" = "Keep in menu bar"; +"views.settings.keeps_in_menu.footer" = "Enable this to keep the app in the menu bar after closing it."; +"views.settings.locks_in_background" = "Lock in background"; +"views.settings.locks_in_background.footer" = "Lock the app with FaceID when sent to the background."; +"views.settings.erase_icloud" = "Erase iCloud store"; +"views.settings.erase_icloud.footer" = "To erase the iCloud store securely, do so on all your synced devices. This will not affect local profiles."; "views.about.title" = "About"; "views.about.sections.resources" = "Resources"; diff --git a/Passepartout/Library/Sources/UILibrary/Theme/UI/Theme+Modifiers.swift b/Passepartout/Library/Sources/UILibrary/Theme/UI/Theme+Modifiers.swift index c9219c12..b6145d69 100644 --- a/Passepartout/Library/Sources/UILibrary/Theme/UI/Theme+Modifiers.swift +++ b/Passepartout/Library/Sources/UILibrary/Theme/UI/Theme+Modifiers.swift @@ -80,7 +80,7 @@ extension View { } public func themeForm() -> some View { - modifier(ThemeFormModifier()) + formStyle(.grouped) } public func themeManualInput() -> some View { @@ -95,6 +95,20 @@ extension View { modifier(ThemeRowWithFooterModifier(footer: footer)) } + public func themeSectionWithSingleRow(header: String? = nil, footer: String, above: Bool = false) -> some View { + Group { + if above { + EmptyView() + .themeRow(footer: footer) + + self + } else { + themeRow(footer: footer) + } + } + .themeSection(header: header, footer: footer) + } + public func themeNavigationDetail() -> some View { #if os(iOS) navigationBarTitleDisplayMode(.inline) @@ -278,13 +292,6 @@ struct ThemeNavigationStackModifier: ViewModifier { // MARK: - Content modifiers -struct ThemeFormModifier: ViewModifier { - func body(content: Content) -> some View { - content - .formStyle(.grouped) - } -} - struct ThemeManualInputModifier: ViewModifier { }