6
6
// Copyright © 2024 Mullvad VPN AB. All rights reserved.
7
7
//
8
8
9
- import Foundation
10
9
import MullvadSettings
11
10
import MullvadTypes
12
11
import UIKit
13
12
14
- class AddLocationsDataSource : UITableViewDiffableDataSource < LocationSection , LocationCellViewModel > {
15
- private let tableView : UITableView
16
- private let nodes : [ LocationNode ]
13
+ class AddLocationsDataSource :
14
+ UITableViewDiffableDataSource < LocationSection , LocationCellViewModel > ,
15
+ LocationDiffableDataSourceProtocol {
17
16
private var customListLocationNode : CustomListLocationNode
17
+ private let nodes : [ LocationNode ]
18
18
var didUpdateCustomList : ( ( CustomListLocationNode ) -> Void ) ?
19
+ let tableView : UITableView
20
+ let sections : [ LocationSection ]
19
21
20
22
init (
21
23
tableView: UITableView ,
22
- allLocations : [ LocationNode ] ,
24
+ allLocationNodes : [ LocationNode ] ,
23
25
customList: CustomList
24
26
) {
25
27
self . tableView = tableView
26
- self . nodes = allLocations
28
+ self . nodes = allLocationNodes
27
29
28
30
self . customListLocationNode = CustomListLocationNodeBuilder (
29
31
customList: customList,
30
32
allLocations: self . nodes
31
33
) . customListLocationNode
32
34
35
+ let sections : [ LocationSection ] = [ . customLists]
36
+ self . sections = sections
37
+
33
38
super. init ( tableView: tableView) { _, indexPath, itemIdentifier in
34
39
let cell = tableView. dequeueReusableView (
35
- withIdentifier: LocationSection . allCases [ indexPath. section] ,
40
+ withIdentifier: sections [ indexPath. section] ,
36
41
for: indexPath
37
- // swiftlint:disable:next force_cast
38
- ) as! LocationCell
42
+ ) as! LocationCell // swiftlint:disable:this force_cast
39
43
cell. configure ( item: itemIdentifier, behavior: . add)
40
44
cell. selectionStyle = . none
41
45
return cell
@@ -47,6 +51,14 @@ class AddLocationsDataSource: UITableViewDiffableDataSource<LocationSection, Loc
47
51
reloadWithSelectedLocations ( )
48
52
}
49
53
54
+ func nodeShowsChildren( _ node: LocationNode ) -> Bool {
55
+ isLocationInCustomList ( node: node)
56
+ }
57
+
58
+ func nodeShouldBeSelected( _ node: LocationNode ) -> Bool {
59
+ customListLocationNode. children. contains ( node)
60
+ }
61
+
50
62
private func reloadWithSelectedLocations( ) {
51
63
var locationsList : [ LocationCellViewModel ] = [ ]
52
64
nodes. forEach { node in
@@ -62,9 +74,13 @@ class AddLocationsDataSource: UITableViewDiffableDataSource<LocationSection, Loc
62
74
return
63
75
}
64
76
65
- // Walk tree backwards to determine which nodes should be expanded.
66
- node. forEachAncestor { node in
67
- node. showsChildren = true
77
+ // Only parents with partially selected children should be expanded.
78
+ node. forEachDescendant { descendantNode in
79
+ if customListLocationNode. children. contains ( descendantNode) {
80
+ descendantNode. forEachAncestor { descendantParentNode in
81
+ descendantParentNode. showsChildren = true
82
+ }
83
+ }
68
84
}
69
85
70
86
locationsList. append ( contentsOf: recursivelyCreateCellViewModelTree (
@@ -73,53 +89,7 @@ class AddLocationsDataSource: UITableViewDiffableDataSource<LocationSection, Loc
73
89
indentationLevel: 1
74
90
) )
75
91
}
76
- updateDataSnapshot ( with: locationsList)
77
- }
78
-
79
- private func updateDataSnapshot(
80
- with list: [ LocationCellViewModel ] ,
81
- animated: Bool = false ,
82
- completion: ( ( ) -> Void ) ? = nil
83
- ) {
84
- var snapshot = NSDiffableDataSourceSnapshot < LocationSection , LocationCellViewModel > ( )
85
-
86
- snapshot. appendSections ( [ . customLists] )
87
- snapshot. appendItems ( list, toSection: . customLists)
88
-
89
- apply ( snapshot, animatingDifferences: animated, completion: completion)
90
- }
91
-
92
- private func recursivelyCreateCellViewModelTree(
93
- for node: LocationNode ,
94
- in section: LocationSection ,
95
- indentationLevel: Int
96
- ) -> [ LocationCellViewModel ] {
97
- var viewModels = [ LocationCellViewModel] ( )
98
- for childNode in node. children {
99
- viewModels. append (
100
- LocationCellViewModel (
101
- section: . customLists,
102
- node: childNode,
103
- indentationLevel: indentationLevel,
104
- isSelected: customListLocationNode. children. contains ( childNode)
105
- )
106
- )
107
-
108
- let indentationLevel = indentationLevel + 1
109
-
110
- // Walk tree forward to determine which nodes should be expanded.
111
- if isLocationInCustomList ( node: childNode) {
112
- viewModels. append (
113
- contentsOf: recursivelyCreateCellViewModelTree (
114
- for: childNode,
115
- in: section,
116
- indentationLevel: indentationLevel
117
- )
118
- )
119
- }
120
- }
121
-
122
- return viewModels
92
+ updateDataSnapshot ( with: [ locationsList] )
123
93
}
124
94
125
95
private func isLocationInCustomList( node: LocationNode ) -> Bool {
@@ -146,26 +116,23 @@ extension AddLocationsDataSource: UITableViewDelegate {
146
116
147
117
extension AddLocationsDataSource : LocationCellDelegate {
148
118
func toggleExpanding( cell: LocationCell ) {
149
- guard let indexPath = tableView. indexPath ( for: cell) ,
150
- let item = itemIdentifier ( for: indexPath) else { return }
151
- let isExpanded = item. node. showsChildren
152
-
153
- item. node. showsChildren = !isExpanded
154
-
155
- var locationList = snapshot ( ) . itemIdentifiers
156
-
157
- if !isExpanded {
158
- locationList. addSubNodes ( from: item, at: indexPath)
159
- } else {
160
- locationList. removeSubNodes ( from: item. node)
119
+ let items = toggledItems ( for: cell) . first!. map { item in
120
+ var item = item
121
+ if containsChild ( parent: customListLocationNode, child: item. node) {
122
+ item. isSelected = true
123
+ }
124
+ return item
161
125
}
162
126
163
- updateDataSnapshot ( with: locationList, animated: true , completion: {
164
- self . scroll ( to: item, animated: true )
127
+ updateDataSnapshot ( with: [ items] , reloadExisting: true , completion: {
128
+ if let indexPath = self . tableView. indexPath ( for: cell) ,
129
+ let item = self . itemIdentifier ( for: indexPath) {
130
+ self . scroll ( to: item, animated: true )
131
+ }
165
132
} )
166
133
}
167
134
168
- func toggleSelection ( cell: LocationCell ) {
135
+ func toggleSelecting ( cell: LocationCell ) {
169
136
guard let index = tableView. indexPath ( for: cell) ? . row else { return }
170
137
171
138
var locationList = snapshot ( ) . itemIdentifiers
@@ -181,36 +148,12 @@ extension AddLocationsDataSource: LocationCellDelegate {
181
148
} else {
182
149
customListLocationNode. remove ( selectedLocation: item. node, with: locationList)
183
150
}
184
- updateDataSnapshot ( with: locationList, completion: {
151
+ updateDataSnapshot ( with: [ locationList] , completion: {
185
152
self . didUpdateCustomList ? ( self . customListLocationNode)
186
153
} )
187
154
}
188
155
}
189
156
190
- extension AddLocationsDataSource {
191
- private func scroll( to item: LocationCellViewModel , animated: Bool ) {
192
- guard
193
- let visibleIndexPaths = tableView. indexPathsForVisibleRows,
194
- let indexPath = indexPath ( for: item)
195
- else { return }
196
-
197
- if item. node. children. count > visibleIndexPaths. count {
198
- tableView. scrollToRow ( at: indexPath, at: . top, animated: animated)
199
- } else {
200
- if let last = item. node. children. last {
201
- if let lastInsertedIndexPath = self . indexPath ( for: LocationCellViewModel (
202
- section: . customLists,
203
- node: last
204
- ) ) ,
205
- let lastVisibleIndexPath = visibleIndexPaths. last,
206
- lastInsertedIndexPath >= lastVisibleIndexPath {
207
- tableView. scrollToRow ( at: lastInsertedIndexPath, at: . bottom, animated: animated)
208
- }
209
- }
210
- }
211
- }
212
- }
213
-
214
157
// MARK: - Toggle selection in table view
215
158
216
159
fileprivate extension [ LocationCellViewModel ] {
0 commit comments