Reuse modifier for navigation stacks with close button (#861)

Refactored:

- iOS
  - VPNProviderServerView
- macOS
  - AboutView
  - ProfileSplitView
- Both
  - ProfileCoordinator
  - VPNProviderServerCoordinator
This commit is contained in:
Davide 2024-11-13 19:17:00 +01:00 committed by GitHub
parent 1b9b9cbd5c
commit 2d698cabfe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 59 additions and 69 deletions

View File

@ -20,7 +20,7 @@ concurrency:
jobs: jobs:
run_tests: run_tests:
name: Run tests name: Run tests
runs-on: macos-14 runs-on: macos-15
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: passepartoutvpn/action-prepare-xcode-build@master - uses: passepartoutvpn/action-prepare-xcode-build@master
@ -30,7 +30,7 @@ jobs:
bundle exec fastlane test bundle exec fastlane test
build_upload: build_upload:
name: Upload to ASC name: Upload to ASC
runs-on: macos-14 runs-on: macos-15
needs: run_tests needs: run_tests
strategy: strategy:
fail-fast: true fail-fast: true

View File

@ -17,7 +17,7 @@ concurrency:
jobs: jobs:
run_swift_tests: run_swift_tests:
name: Run SwiftPM tests name: Run SwiftPM tests
runs-on: macos-14 runs-on: macos-15
timeout-minutes: 10 timeout-minutes: 10
steps: steps:
- uses: passepartoutvpn/action-prepare-xcode-build@master - uses: passepartoutvpn/action-prepare-xcode-build@master

View File

@ -30,23 +30,13 @@ import SwiftUI
extension AboutRouterView { extension AboutRouterView {
var body: some View { var body: some View {
NavigationStack(path: $path) { AboutView(
AboutView( profileManager: profileManager,
profileManager: profileManager, navigationRoute: $navigationRoute
navigationRoute: $navigationRoute )
) .navigationDestination(for: NavigationRoute.self, destination: pushDestination)
.toolbar { .themeNavigationDetail()
ToolbarItem(placement: .cancellationAction) { .themeNavigationStack(closable: true, path: $path)
Button {
dismiss()
} label: {
ThemeImage(.close)
}
}
}
.navigationDestination(for: NavigationRoute.self, destination: pushDestination)
.themeNavigationDetail()
}
} }
} }

View File

@ -36,17 +36,16 @@ extension AboutRouterView {
navigationRoute: $navigationRoute navigationRoute: $navigationRoute
) )
} detail: { } detail: {
NavigationStack(path: $path) { pushDestination(for: navigationRoute)
pushDestination(for: navigationRoute) .navigationDestination(for: NavigationRoute.self, destination: pushDestination)
.navigationDestination(for: NavigationRoute.self, destination: pushDestination) .themeNavigationStack(closable: false, path: $path)
} .toolbar {
.toolbar { ToolbarItem(placement: .confirmationAction) {
ToolbarItem(placement: .confirmationAction) { Button(Strings.Global.ok) {
Button(Strings.Global.ok) { dismiss()
dismiss() }
} }
} }
}
} }
.onLoad { .onLoad {
navigationRoute = .links navigationRoute = .links

View File

@ -55,7 +55,7 @@ struct ProfileSplitView: View, Routable {
flow: flow flow: flow
) )
} detail: { } detail: {
NavigationStack(path: $detailPath) { Group {
switch selectedModuleId { switch selectedModuleId {
case ModuleListView.generalModuleId: case ModuleListView.generalModuleId:
detailView(for: .general) detailView(for: .general)
@ -64,6 +64,7 @@ struct ProfileSplitView: View, Routable {
detailView(for: .module(id: selectedModuleId)) detailView(for: .module(id: selectedModuleId))
} }
} }
.themeNavigationStack(path: $detailPath)
.toolbar(content: toolbarContent) .toolbar(content: toolbarContent)
.environment(\.navigationPath, $detailPath) .environment(\.navigationPath, $detailPath)
} }

View File

@ -44,25 +44,15 @@ struct VPNProviderServerCoordinator<Configuration>: View where Configuration: Pr
let onSelect: (VPNEntity<Configuration>) async throws -> Void let onSelect: (VPNEntity<Configuration>) async throws -> Void
var body: some View { var body: some View {
NavigationStack { VPNProviderServerView(
VPNProviderServerView( moduleId: moduleId,
moduleId: moduleId, providerId: providerId,
providerId: providerId, configurationType: Configuration.self,
configurationType: Configuration.self, selectedEntity: selectedEntity,
selectedEntity: selectedEntity, filtersWithSelection: false,
filtersWithSelection: false, onSelect: onSelect
onSelect: onSelect )
) .themeNavigationStack(closable: true)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button {
dismiss()
} label: {
ThemeCloseLabel()
}
}
}
}
} }
} }

View File

@ -79,7 +79,7 @@ private extension VPNProviderServerView {
Button { Button {
isPresented = false isPresented = false
} label: { } label: {
ThemeImage(.close) ThemeCloseLabel()
} }
} }
} }

View File

@ -44,7 +44,7 @@ public struct SettingsView: View {
.navigationTitle(Strings.Global.settings) .navigationTitle(Strings.Global.settings)
#if os(iOS) #if os(iOS)
.themeNavigationDetail() .themeNavigationDetail()
.themeNavigationStack(if: true, closable: true, path: $path) .themeNavigationStack(closable: true, path: $path)
#endif #endif
} }
} }

View File

@ -75,8 +75,24 @@ extension View {
)) ))
} }
public func themeNavigationStack(if condition: Bool, closable: Bool = false, path: Binding<NavigationPath>) -> some View { public func themeNavigationStack(
modifier(ThemeNavigationStackModifier(condition: condition, closable: closable, path: path)) closable: Bool = false,
path: Binding<NavigationPath> = .constant(NavigationPath())
) -> some View {
modifier(ThemeNavigationStackModifier(closable: closable, path: path))
}
@ViewBuilder
public func themeNavigationStack(
if condition: Bool,
closable: Bool = false,
path: Binding<NavigationPath> = .constant(NavigationPath())
) -> some View {
if condition {
modifier(ThemeNavigationStackModifier(closable: closable, path: path))
} else {
self
}
} }
public func themeForm() -> some View { public func themeForm() -> some View {
@ -269,31 +285,25 @@ struct ThemeNavigationStackModifier: ViewModifier {
@Environment(\.dismiss) @Environment(\.dismiss)
private var dismiss private var dismiss
let condition: Bool
let closable: Bool let closable: Bool
@Binding @Binding
var path: NavigationPath var path: NavigationPath
func body(content: Content) -> some View { func body(content: Content) -> some View {
if condition { NavigationStack(path: $path) {
NavigationStack(path: $path) { content
content .toolbar {
.toolbar { if closable {
if closable { ToolbarItem(placement: .cancellationAction) {
ToolbarItem(placement: .cancellationAction) { Button {
Button { dismiss()
dismiss() } label: {
} label: { ThemeCloseLabel()
ThemeCloseLabel()
}
} }
} }
} }
} }
} else {
content
} }
} }
} }