diff --git a/Tusker/Screens/Profile/ProfileTableViewController.swift b/Tusker/Screens/Profile/ProfileTableViewController.swift index c826472895..4295b1a980 100644 --- a/Tusker/Screens/Profile/ProfileTableViewController.swift +++ b/Tusker/Screens/Profile/ProfileTableViewController.swift @@ -22,6 +22,13 @@ class ProfileTableViewController: EnhancedTableViewController { } } + var pinnedStatusIDs: [String] = [] { + didSet { + DispatchQueue.main.async { + self.tableView.reloadData() + } + } + } var timelineSegments: [TimelineSegment] = [] { didSet { DispatchQueue.main.async { @@ -98,6 +105,13 @@ class ProfileTableViewController: EnhancedTableViewController { updateUIForPreferences() + getStatuses(onlyPinned: true) { (response) in + guard case let .success(statuses, _) = response else { fatalError() } + + MastodonCache.addAll(statuses: statuses) + self.pinnedStatusIDs = statuses.map { $0.id } + } + getStatuses() { response in guard case let .success(statuses, pagination) = response else { fatalError() } @@ -114,8 +128,8 @@ class ProfileTableViewController: EnhancedTableViewController { navigationItem.title = account.realDisplayName } - func getStatuses(for range: RequestRange = .default, completion: @escaping Client.Callback<[Status]>) { - let request = Account.getStatuses(accountID, range: range, onlyMedia: false, pinned: false, excludeReplies: !Preferences.shared.showRepliesInProfiles) + func getStatuses(for range: RequestRange = .default, onlyPinned: Bool = false, completion: @escaping Client.Callback<[Status]>) { + let request = Account.getStatuses(accountID, range: range, onlyMedia: false, pinned: onlyPinned, excludeReplies: !Preferences.shared.showRepliesInProfiles) MastodonController.client.run(request, completion: completion) } @@ -128,27 +142,38 @@ class ProfileTableViewController: EnhancedTableViewController { // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { - return 1 + timelineSegments.count + // 1 section for header, 1 section for pinned, rest for timeline + return 2 + timelineSegments.count } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { if section == 0 { return accountID == nil || MastodonCache.account(for: accountID) == nil ? 0 : 1 + } else if section == 1 { + return pinnedStatusIDs.count } else { - return timelineSegments[section - 1].count + return timelineSegments[section - 2].count } } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - if indexPath.section == 0 { + switch indexPath.section { + case 0: guard let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as? ProfileHeaderTableViewCell else { fatalError() } cell.selectionStyle = .none cell.delegate = self cell.updateUI(for: accountID) return cell - } else { + case 1: guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() } - let statusID = timelineSegments[indexPath.section - 1][indexPath.row] + let statusID = pinnedStatusIDs[indexPath.row] + cell.showPinned = true + cell.updateUI(statusID: statusID) + cell.delegate = self + return cell + default: + guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() } + let statusID = timelineSegments[indexPath.section - 2][indexPath.row] cell.updateUI(statusID: statusID) cell.delegate = self return cell @@ -156,14 +181,14 @@ class ProfileTableViewController: EnhancedTableViewController { } override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { - if timelineSegments.count > 0 && indexPath.section == timelineSegments.count && indexPath.row == timelineSegments[indexPath.section - 1].count - 1 { + if timelineSegments.count > 0 && indexPath.section - 1 == timelineSegments.count && indexPath.row == timelineSegments[indexPath.section - 2].count - 1 { guard let older = older else { return } getStatuses(for: older) { response in guard case let .success(newStatuses, pagination) = response else { fatalError() } MastodonCache.addAll(statuses: newStatuses) - self.timelineSegments[indexPath.section - 1].append(objects: newStatuses) + self.timelineSegments[indexPath.section - 2].append(objects: newStatuses) self.older = pagination?.older } @@ -246,8 +271,8 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate { extension ProfileTableViewController: UITableViewDataSourcePrefetching { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { - for indexPath in indexPaths where indexPath.section > 0 { - let statusID = timelineSegments[indexPath.section - 1][indexPath.row] + for indexPath in indexPaths where indexPath.section > 1 { + let statusID = timelineSegments[indexPath.section - 2][indexPath.row] guard let status = MastodonCache.status(for: statusID) else { continue } ImageCache.avatars.get(status.account.avatar, completion: nil) for attachment in status.attachments { @@ -257,8 +282,8 @@ extension ProfileTableViewController: UITableViewDataSourcePrefetching { } func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) { - for indexPath in indexPaths where indexPath.section > 0 { - let statusID = timelineSegments[indexPath.section - 1][indexPath.row] + for indexPath in indexPaths where indexPath.section > 1 { + let statusID = timelineSegments[indexPath.section - 2][indexPath.row] guard let status = MastodonCache.status(for: statusID) else { continue } ImageCache.avatars.cancel(status.account.avatar) for attachment in status.attachments { diff --git a/Tusker/Views/Status/StatusTableViewCell.swift b/Tusker/Views/Status/StatusTableViewCell.swift index 126eaa1315..45395059ab 100644 --- a/Tusker/Views/Status/StatusTableViewCell.swift +++ b/Tusker/Views/Status/StatusTableViewCell.swift @@ -42,6 +42,7 @@ class StatusTableViewCell: UITableViewCell { @IBOutlet weak var favoriteButton: UIButton! @IBOutlet weak var reblogButton: UIButton! @IBOutlet weak var moreButton: UIButton! + @IBOutlet weak var pinnedStackView: UIStackView! var statusID: String! var accountID: String! @@ -58,6 +59,7 @@ class StatusTableViewCell: UITableViewCell { reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor } } + var showPinned: Bool = false var collapsible = false { didSet { @@ -153,6 +155,9 @@ class StatusTableViewCell: UITableViewCell { setCollapsed(collapsible, animated: false) contentWarningLabel.text = status.spoilerText contentWarningLabel.isHidden = status.spoilerText.isEmpty + + let pinned = status.pinned ?? false + pinnedStackView.isHidden = !(pinned && showPinned) } private func updateStatusState(status: Status) { @@ -226,6 +231,7 @@ class StatusTableViewCell: UITableViewCell { updateTimestampWorkItem?.cancel() updateTimestampWorkItem = nil attachmentsView.subviews.forEach { $0.removeFromSuperview() } + showPinned = false } override func setSelected(_ selected: Bool, animated: Bool) { diff --git a/Tusker/Views/Status/StatusTableViewCell.xib b/Tusker/Views/Status/StatusTableViewCell.xib index 39a98ca394..032880d7e6 100644 --- a/Tusker/Views/Status/StatusTableViewCell.xib +++ b/Tusker/Views/Status/StatusTableViewCell.xib @@ -1,8 +1,8 @@ - + - + @@ -16,6 +16,21 @@ + - + @@ -37,7 +52,7 @@ - + @@ -101,7 +116,7 @@ - +