-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
BackForwardListViewController.swift
249 lines (212 loc) · 9.11 KB
/
BackForwardListViewController.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import UIKit
import Shared
import WebKit
import Storage
import SnapKit
private struct BackForwardViewUX {
static let RowHeight: CGFloat = 50
static let BackgroundColor = UIColor.Photon.Grey10.withAlphaComponent(0.4)
}
class BackForwardListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UIGestureRecognizerDelegate {
fileprivate let BackForwardListCellIdentifier = "BackForwardListViewController"
fileprivate var profile: Profile
fileprivate lazy var sites = [String: Site]()
fileprivate var dismissing = false
fileprivate var currentRow = 0
fileprivate var verticalConstraints: [Constraint] = []
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.separatorStyle = .none
tableView.dataSource = self
tableView.delegate = self
tableView.alwaysBounceVertical = false
tableView.register(BackForwardTableViewCell.self, forCellReuseIdentifier: self.BackForwardListCellIdentifier)
tableView.backgroundColor = BackForwardViewUX.BackgroundColor
let blurEffect = UIBlurEffect(style: .extraLight)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
tableView.backgroundView = blurEffectView
return tableView
}()
lazy var shadow: UIView = {
let shadow = UIView()
shadow.backgroundColor = UIColor(white: 0, alpha: 0.2)
return shadow
}()
var tabManager: TabManager!
weak var bvc: BrowserViewController?
var currentItem: WKBackForwardListItem?
var listData = [WKBackForwardListItem]()
var tableHeight: CGFloat {
get {
assert(Thread.isMainThread, "tableHeight interacts with UIKit components - cannot call from background thread.")
return min(BackForwardViewUX.RowHeight * CGFloat(listData.count), self.view.frame.height/2)
}
}
var backForwardTransitionDelegate: UIViewControllerTransitioningDelegate? {
didSet {
self.transitioningDelegate = backForwardTransitionDelegate
}
}
var snappedToBottom: Bool = true
init(profile: Profile, backForwardList: WKBackForwardList) {
self.profile = profile
super.init(nibName: nil, bundle: nil)
loadSites(backForwardList)
loadSitesFromProfile()
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(shadow)
view.addSubview(tableView)
snappedToBottom = bvc?.toolbar != nil
tableView.snp.makeConstraints { make in
make.height.equalTo(0)
make.left.right.equalTo(self.view)
}
shadow.snp.makeConstraints { make in
make.left.right.equalTo(self.view)
}
remakeVerticalConstraints()
view.layoutIfNeeded()
scrollTableViewToIndex(currentRow)
setupDismissTap()
}
func loadSitesFromProfile() {
let sql = profile.favicons as! SQLiteHistory
let urls = listData.compactMap {$0.url.isLocal ? $0.url.getQuery()["url"]?.unescape() : $0.url.absoluteString}
sql.getSitesForURLs(urls).uponQueue(.main) { result in
guard let results = result.successValue else {
return
}
// Add all results into the sites dictionary
results.compactMap({$0}).forEach({site in
if let url = site?.url {
self.sites[url] = site
}
})
self.tableView.reloadData()
}
}
func homeAndNormalPagesOnly(_ bfList: WKBackForwardList) {
let items = bfList.forwardList.reversed() + [bfList.currentItem].compactMap({$0}) + bfList.backList.reversed()
//error url's are OK as they are used to populate history on session restore.
listData = items.filter({return !($0.url.isLocal && ($0.url.originalURLFromErrorURL?.isLocal ?? true)) || $0.url.isAboutHomeURL})
}
func loadSites(_ bfList: WKBackForwardList) {
currentItem = bfList.currentItem
homeAndNormalPagesOnly(bfList)
}
func scrollTableViewToIndex(_ index: Int) {
guard index > 1 else {
return
}
let moveToIndexPath = IndexPath(row: index-2, section: 0)
tableView.reloadRows(at: [moveToIndexPath], with: .none)
tableView.scrollToRow(at: moveToIndexPath, at: .middle, animated: false)
}
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
guard let bvc = self.bvc else {
return
}
if bvc.shouldShowFooterForTraitCollection(newCollection) != snappedToBottom {
tableView.snp.updateConstraints { make in
if snappedToBottom {
make.bottom.equalTo(self.view).offset(0)
} else {
make.top.equalTo(self.view).offset(0)
}
make.height.equalTo(0)
}
snappedToBottom = !snappedToBottom
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
let correctHeight = {
self.tableView.snp.updateConstraints { make in
make.height.equalTo(min(BackForwardViewUX.RowHeight * CGFloat(self.listData.count), size.height / 2))
}
}
coordinator.animate(alongsideTransition: nil) { _ in
self.remakeVerticalConstraints()
correctHeight()
}
}
func remakeVerticalConstraints() {
guard let bvc = self.bvc else {
return
}
for constraint in self.verticalConstraints {
constraint.deactivate()
}
self.verticalConstraints = []
tableView.snp.makeConstraints { make in
if snappedToBottom {
verticalConstraints += [make.bottom.equalTo(self.view).offset(-bvc.footer.frame.height).constraint]
} else {
verticalConstraints += [make.top.equalTo(self.view).offset(bvc.header.frame.height + UIApplication.shared.statusBarFrame.size.height).constraint]
}
}
shadow.snp.makeConstraints() { make in
if snappedToBottom {
verticalConstraints += [
make.bottom.equalTo(tableView.snp.top).constraint,
make.top.equalTo(self.view).constraint
]
} else {
verticalConstraints += [
make.top.equalTo(tableView.snp.bottom).constraint,
make.bottom.equalTo(self.view).constraint
]
}
}
}
func setupDismissTap() {
let tap = UITapGestureRecognizer(target: self, action: #selector(handleTap))
tap.cancelsTouchesInView = false
tap.delegate = self
view.addGestureRecognizer(tap)
}
@objc func handleTap() {
dismiss(animated: true, completion: nil)
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if touch.view?.isDescendant(of: tableView) ?? true {
return false
}
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return listData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: BackForwardListCellIdentifier, for: indexPath) as! BackForwardTableViewCell
let item = listData[indexPath.item]
let urlString = item.url.isLocal ? item.url.getQuery()["url"]?.unescape() : item.url.absoluteString
cell.isCurrentTab = listData[indexPath.item] == self.currentItem
cell.connectingBackwards = indexPath.item != listData.count-1
cell.connectingForwards = indexPath.item != 0
guard let url = urlString, !item.url.isAboutHomeURL else {
cell.site = Site(url: item.url.absoluteString, title: Strings.FirefoxHomePage)
return cell
}
cell.site = sites[url] ?? Site(url: url, title: item.title ?? "")
cell.setNeedsDisplay()
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tabManager.selectedTab?.goToBackForwardListItem(listData[indexPath.item])
dismiss(animated: true, completion: nil)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return BackForwardViewUX.RowHeight
}
}