-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
NavigationLinkStore.swift
132 lines (125 loc) · 4.59 KB
/
NavigationLinkStore.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import SwiftUI
@available(iOS, introduced: 13.0, deprecated: 16.0)
@available(macOS, introduced: 10.15, deprecated: 13.0)
@available(tvOS, introduced: 13.0, deprecated: 16.0)
@available(watchOS, introduced: 6.0, deprecated: 9.0)
public struct NavigationLinkStore<
State,
Action,
DestinationState,
DestinationAction,
Destination: View,
Label: View
>: View {
let store: Store<PresentationState<State>, PresentationAction<Action>>
@ObservedObject var viewStore: ViewStore<Bool, PresentationAction<Action>>
let toDestinationState: (State) -> DestinationState?
let fromDestinationAction: (DestinationAction) -> Action
let onTap: () -> Void
let destination: (Store<DestinationState, DestinationAction>) -> Destination
let label: Label
var isDetailLink = true
public init(
_ store: Store<PresentationState<State>, PresentationAction<Action>>,
onTap: @escaping () -> Void,
@ViewBuilder destination: @escaping (Store<State, Action>) -> Destination,
@ViewBuilder label: () -> Label
) where State == DestinationState, Action == DestinationAction {
let filteredStore = store.filterSend { state, _ in state.wrappedValue != nil }
self.store = filteredStore
self.viewStore = ViewStore(filteredStore.scope(state: { $0.wrappedValue != nil }))
self.toDestinationState = { $0 }
self.fromDestinationAction = { $0 }
self.onTap = onTap
self.destination = destination
self.label = label()
}
public init(
_ store: Store<PresentationState<State>, PresentationAction<Action>>,
state toDestinationState: @escaping (State) -> DestinationState?,
action fromDestinationAction: @escaping (DestinationAction) -> Action,
onTap: @escaping () -> Void,
@ViewBuilder destination: @escaping (Store<DestinationState, DestinationAction>) -> Destination,
@ViewBuilder label: () -> Label
) {
self.store = store
self.viewStore = ViewStore(
store
.filterSend { state, _ in state.wrappedValue != nil }
.scope(state: { $0.wrappedValue.flatMap(toDestinationState) != nil })
)
self.toDestinationState = toDestinationState
self.fromDestinationAction = fromDestinationAction
self.onTap = onTap
self.destination = destination
self.label = label()
}
public init(
_ store: Store<PresentationState<State>, PresentationAction<Action>>,
id: State.ID,
onTap: @escaping () -> Void,
@ViewBuilder destination: @escaping (Store<State, Action>) -> Destination,
@ViewBuilder label: () -> Label
) where State == DestinationState, Action == DestinationAction, State: Identifiable {
let filteredStore = store.filterSend { state, _ in state.wrappedValue != nil }
self.store = filteredStore
self.viewStore = ViewStore(filteredStore.scope(state: { $0.wrappedValue?.id == id }))
self.toDestinationState = { $0 }
self.fromDestinationAction = { $0 }
self.onTap = onTap
self.destination = destination
self.label = label()
}
public init(
_ store: Store<PresentationState<State>, PresentationAction<Action>>,
state toDestinationState: @escaping (State) -> DestinationState?,
action fromDestinationAction: @escaping (DestinationAction) -> Action,
id: DestinationState.ID,
onTap: @escaping () -> Void,
@ViewBuilder destination: @escaping (Store<DestinationState, DestinationAction>) -> Destination,
@ViewBuilder label: () -> Label
) where DestinationState: Identifiable {
self.store = store
self.viewStore = ViewStore(
store
.filterSend { state, _ in state.wrappedValue != nil }
.scope(state: { $0.wrappedValue.flatMap(toDestinationState)?.id == id })
)
self.toDestinationState = toDestinationState
self.fromDestinationAction = fromDestinationAction
self.onTap = onTap
self.destination = destination
self.label = label()
}
public var body: some View {
NavigationLink(
// TODO: construct better binding that handles animation
isActive: Binding(
get: { self.viewStore.state },
set: {
if $0 {
self.onTap()
} else if self.viewStore.state {
self.viewStore.send(.dismiss)
}
}
)
) {
IfLetStore(
self.store.scope(
state: returningLastNonNilValue { $0.wrappedValue.flatMap(self.toDestinationState) },
action: { .presented(self.fromDestinationAction($0)) }
),
then: self.destination
)
} label: {
self.label
}
.isDetailLink(self.isDetailLink)
}
public func isDetailLink(_ isDetailLink: Bool) -> Self {
var link = self
link.isDetailLink = isDetailLink
return link
}
}