Reuse modifier for navigation stacks with close button (#861)
Refactored: - iOS - VPNProviderServerView - macOS - AboutView - ProfileSplitView - Both - ProfileCoordinator - VPNProviderServerCoordinator
This commit is contained in:
parent
1b9b9cbd5c
commit
2d698cabfe
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,7 @@ private extension VPNProviderServerView {
|
||||||
Button {
|
Button {
|
||||||
isPresented = false
|
isPresented = false
|
||||||
} label: {
|
} label: {
|
||||||
ThemeImage(.close)
|
ThemeCloseLabel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue