Compare commits
4 Commits
24a1e7ceb9
...
06442b5629
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 06442b5629 | |
Shadowfacts | d5232c0b03 | |
Shadowfacts | 7140590ccf | |
Shadowfacts | b47b08fa95 |
|
@ -12,12 +12,18 @@ public class NotificationGroup {
|
|||
public let notificationIDs: [String]
|
||||
public let id: String
|
||||
public let kind: Notification.Kind
|
||||
public let statusState: StatusState?
|
||||
|
||||
init?(notifications: [Notification]) {
|
||||
guard !notifications.isEmpty else { return nil }
|
||||
self.notificationIDs = notifications.map { $0.id }
|
||||
self.id = notifications.first!.id
|
||||
self.kind = notifications.first!.kind
|
||||
if kind == .mention {
|
||||
self.statusState = .unknown
|
||||
} else {
|
||||
self.statusState = nil
|
||||
}
|
||||
}
|
||||
|
||||
public static func createGroups(notifications: [Notification], only allowedTypes: [Notification.Kind]) -> [NotificationGroup] {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// StatusState.swift
|
||||
// Pachyderm
|
||||
//
|
||||
// Created by Shadowfacts on 11/24/19.
|
||||
// Copyright © 2019 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class StatusState: Equatable, Hashable {
|
||||
public var collapsible: Bool?
|
||||
public var collapsed: Bool?
|
||||
|
||||
public var unknown: Bool {
|
||||
collapsible == nil || collapsed == nil
|
||||
}
|
||||
|
||||
public init(collapsible: Bool?, collapsed: Bool?) {
|
||||
self.collapsible = collapsible
|
||||
self.collapsed = collapsed
|
||||
}
|
||||
|
||||
public func copy() -> StatusState {
|
||||
return StatusState(collapsible: self.collapsible, collapsed: self.collapsed)
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(collapsible)
|
||||
hasher.combine(collapsed)
|
||||
}
|
||||
|
||||
public static var unknown: StatusState {
|
||||
StatusState(collapsible: nil, collapsed: nil)
|
||||
}
|
||||
|
||||
public static func == (lhs: StatusState, rhs: StatusState) -> Bool {
|
||||
lhs.collapsible == rhs.collapsible && lhs.collapsed == rhs.collapsed
|
||||
}
|
||||
}
|
|
@ -90,6 +90,7 @@
|
|||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
|
||||
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.swift */; };
|
||||
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
|
||||
D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; };
|
||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
|
||||
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; };
|
||||
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */; };
|
||||
|
@ -285,6 +286,7 @@
|
|||
04DACE8D212CC7CC009840C4 /* ImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCache.swift; sourceTree = "<group>"; };
|
||||
04ED00B021481ED800567C53 /* SteppedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SteppedProgressView.swift; sourceTree = "<group>"; };
|
||||
D6028B9A2150811100F223B9 /* MastodonCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonCache.swift; sourceTree = "<group>"; };
|
||||
D60A4FFB238B726A008AC647 /* StatusState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusState.swift; sourceTree = "<group>"; };
|
||||
D60A548B21ED515800F1F87C /* GMImagePicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GMImagePicker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
D60A548D21ED515800F1F87C /* GMImagePicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GMImagePicker.h; sourceTree = "<group>"; };
|
||||
D60A548E21ED515800F1F87C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -1000,6 +1002,7 @@
|
|||
D6A3BC7223218C6E00FD64D5 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D60A4FFB238B726A008AC647 /* StatusState.swift */,
|
||||
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */,
|
||||
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */,
|
||||
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */,
|
||||
|
@ -1534,6 +1537,7 @@
|
|||
D61099F5214568C300432DC2 /* Notification.swift in Sources */,
|
||||
D61099EF214566C000432DC2 /* Instance.swift in Sources */,
|
||||
D61099D22144B2E600432DC2 /* Body.swift in Sources */,
|
||||
D63569E023908A8D003DD353 /* StatusState.swift in Sources */,
|
||||
D6109A0121456B0800432DC2 /* Hashtag.swift in Sources */,
|
||||
D61099FD21456A1D00432DC2 /* SearchResults.swift in Sources */,
|
||||
D61099F12145686D00432DC2 /* List.swift in Sources */,
|
||||
|
|
|
@ -15,8 +15,9 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
static let showPostsImage = UIImage(systemName: "eye.fill")!
|
||||
static let hidePostsImage = UIImage(systemName: "eye.slash.fill")!
|
||||
|
||||
var mainStatusID: String!
|
||||
var statusIDs: [String] = [] {
|
||||
let mainStatusID: String
|
||||
let mainStatusState: StatusState
|
||||
var statuses: [(id: String, state: StatusState)] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
|
@ -27,8 +28,9 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
var showStatusesAutomatically = false
|
||||
var visibilityBarButtonItem: UIBarButtonItem!
|
||||
|
||||
init(for mainStatusID: String) {
|
||||
init(for mainStatusID: String, state: StatusState = .unknown) {
|
||||
self.mainStatusID = mainStatusID
|
||||
self.mainStatusState = state
|
||||
|
||||
super.init(style: .plain)
|
||||
}
|
||||
|
@ -51,9 +53,9 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
visibilityBarButtonItem = UIBarButtonItem(image: ConversationTableViewController.showPostsImage, style: .plain, target: self, action: #selector(toggleVisibilityButtonPressed))
|
||||
navigationItem.rightBarButtonItem = visibilityBarButtonItem
|
||||
|
||||
statusIDs = [mainStatusID]
|
||||
|
||||
guard let mainStatus = MastodonCache.status(for: mainStatusID) else { fatalError("Missing cached status \(mainStatusID!)") }
|
||||
statuses = [(mainStatusID, mainStatusState)]
|
||||
|
||||
guard let mainStatus = MastodonCache.status(for: mainStatusID) else { fatalError("Missing cached status \(mainStatusID)") }
|
||||
|
||||
let request = Status.getContext(mainStatus)
|
||||
MastodonController.client.run(request) { response in
|
||||
|
@ -61,8 +63,8 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
let parents = self.getDirectParents(of: mainStatus, from: context.ancestors)
|
||||
MastodonCache.addAll(statuses: parents)
|
||||
MastodonCache.addAll(statuses: context.descendants)
|
||||
self.statusIDs = parents.map { $0.id } + [self.mainStatusID] + context.descendants.map { $0.id }
|
||||
let indexPath = IndexPath(row: self.statusIDs.firstIndex(of: self.mainStatusID)!, section: 0)
|
||||
self.statuses = parents.map { ($0.id, .unknown) } + self.statuses + context.descendants.map { ($0.id, .unknown) }
|
||||
let indexPath = IndexPath(row: parents.count, section: 0)
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
|
||||
}
|
||||
|
@ -89,30 +91,30 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return statusIDs.count
|
||||
return statuses.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let statusID = statusIDs[indexPath.row]
|
||||
let (id, state) = statuses[indexPath.row]
|
||||
|
||||
if statusID == mainStatusID {
|
||||
if id == mainStatusID {
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "mainStatusCell", for: indexPath) as? ConversationMainStatusTableViewCell else { fatalError() }
|
||||
cell.selectionStyle = .none
|
||||
cell.showStatusAutomatically = showStatusesAutomatically
|
||||
cell.updateUI(statusID: statusID)
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
} else {
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
||||
cell.showStatusAutomatically = showStatusesAutomatically
|
||||
cell.updateUI(statusID: statusID)
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||
let statusID = statusIDs[indexPath.row]
|
||||
let statusID = statuses[indexPath.row].id
|
||||
return statusID == mainStatusID ? nil : indexPath
|
||||
}
|
||||
|
||||
|
@ -131,14 +133,21 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
@objc func toggleVisibilityButtonPressed() {
|
||||
showStatusesAutomatically = !showStatusesAutomatically
|
||||
|
||||
for (_, state) in statuses where state.collapsible == true {
|
||||
state.collapsed = !showStatusesAutomatically
|
||||
}
|
||||
|
||||
for cell in tableView.visibleCells {
|
||||
guard let cell = cell as? BaseStatusTableViewCell,
|
||||
cell.collapsible else { continue }
|
||||
cell.showStatusAutomatically = showStatusesAutomatically
|
||||
cell.setCollapsed(!showStatusesAutomatically, animated: false)
|
||||
}
|
||||
statusCollapsedStateChanged()
|
||||
|
||||
// recalculate cell heights
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
||||
if showStatusesAutomatically {
|
||||
visibilityBarButtonItem.image = ConversationTableViewController.hidePostsImage
|
||||
} else {
|
||||
|
@ -149,7 +158,7 @@ class ConversationTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension ConversationTableViewController: StatusTableViewCellDelegate {
|
||||
func statusCollapsedStateChanged() {
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
// causes the table view to recalculate the cell heights
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
@ -159,7 +168,7 @@ extension ConversationTableViewController: StatusTableViewCellDelegate {
|
|||
extension ConversationTableViewController: UITableViewDataSourcePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let status = MastodonCache.status(for: statusIDs[indexPath.row]) else { continue }
|
||||
guard let status = MastodonCache.status(for: statuses[indexPath.row].id) else { continue }
|
||||
ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments {
|
||||
ImageCache.attachments.get(attachment.url, completion: nil)
|
||||
|
@ -169,7 +178,7 @@ extension ConversationTableViewController: UITableViewDataSourcePrefetching {
|
|||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths {
|
||||
guard let status = MastodonCache.status(for: statusIDs[indexPath.row]) else { continue }
|
||||
guard let status = MastodonCache.status(for: statuses[indexPath.row].id) else { continue }
|
||||
ImageCache.avatars.cancel(status.account.avatar)
|
||||
for attachment in status.attachments {
|
||||
ImageCache.attachments.cancel(attachment.url)
|
||||
|
|
|
@ -71,16 +71,6 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
|||
modalPresentationStyle = .fullScreen
|
||||
}
|
||||
|
||||
// init(gifData: Data?, description: String?, sourceFrame: CGRect, sourceCornerRadius: CGFloat, router: AppRouter) {
|
||||
// self.router = router
|
||||
// self.gifData = gifData
|
||||
// self.imageDescription = description
|
||||
// self.originFrame = sourceFrame
|
||||
// self.originCornerRadius = sourceCornerRadius
|
||||
//
|
||||
// super.init(nibName: "LargeImageViewController", bundle: nil)
|
||||
// }
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
@ -172,9 +162,9 @@ class LargeImageViewController: UIViewController, UIScrollViewDelegate {
|
|||
|
||||
let prevZoomScale = self.prevZoomScale ?? scrollView.minimumZoomScale
|
||||
if scrollView.zoomScale <= scrollView.minimumZoomScale {
|
||||
controlsVisible = true
|
||||
setControlsVisible(true, animated: true)
|
||||
} else if scrollView.zoomScale > prevZoomScale {
|
||||
controlsVisible = false
|
||||
setControlsVisible(false, animated: true)
|
||||
}
|
||||
self.prevZoomScale = scrollView.zoomScale
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
|||
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as? TimelineStatusTableViewCell else {
|
||||
fatalError()
|
||||
}
|
||||
cell.updateUI(statusID: notification.status!.id)
|
||||
cell.updateUI(statusID: notification.status!.id, state: group.statusState!)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
|
||||
|
@ -212,7 +212,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension NotificationsTableViewController: StatusTableViewCellDelegate {
|
||||
func statusCollapsedStateChanged() {
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
// causes the table view to recalculate the cell heights
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
|
|
@ -22,14 +22,14 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
var pinnedStatusIDs: [String] = [] {
|
||||
var pinnedStatuses: [(id: String, state: StatusState)] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
var timelineSegments: [TimelineSegment<Status>] = [] {
|
||||
var timelineSegments: [[(id: String, state: StatusState)]] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
|
@ -109,14 +109,14 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
guard case let .success(statuses, _) = response else { fatalError() }
|
||||
|
||||
MastodonCache.addAll(statuses: statuses)
|
||||
self.pinnedStatusIDs = statuses.map { $0.id }
|
||||
self.pinnedStatuses = statuses.map { ($0.id, .unknown) }
|
||||
}
|
||||
|
||||
getStatuses() { response in
|
||||
guard case let .success(statuses, pagination) = response else { fatalError() }
|
||||
|
||||
MastodonCache.addAll(statuses: statuses)
|
||||
self.timelineSegments.append(TimelineSegment(objects: statuses))
|
||||
self.timelineSegments.append(statuses.map { ($0.id, .unknown) })
|
||||
|
||||
self.older = pagination?.older
|
||||
self.newer = pagination?.newer
|
||||
|
@ -150,7 +150,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
if section == 0 {
|
||||
return accountID == nil || MastodonCache.account(for: accountID) == nil ? 0 : 1
|
||||
} else if section == 1 {
|
||||
return pinnedStatusIDs.count
|
||||
return pinnedStatuses.count
|
||||
} else {
|
||||
return timelineSegments[section - 2].count
|
||||
}
|
||||
|
@ -166,15 +166,15 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
return cell
|
||||
case 1:
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
||||
let statusID = pinnedStatusIDs[indexPath.row]
|
||||
let (id, state) = pinnedStatuses[indexPath.row]
|
||||
cell.showPinned = true
|
||||
cell.updateUI(statusID: statusID)
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
default:
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row]
|
||||
cell.updateUI(statusID: statusID)
|
||||
let (id, state) = timelineSegments[indexPath.section - 2][indexPath.row]
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||
|
||||
MastodonCache.addAll(statuses: newStatuses)
|
||||
self.timelineSegments[indexPath.section - 2].append(objects: newStatuses)
|
||||
self.timelineSegments[indexPath.section - 2].append(contentsOf: newStatuses.map { ($0.id, .unknown) })
|
||||
|
||||
self.older = pagination?.older
|
||||
}
|
||||
|
@ -214,7 +214,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||
|
||||
MastodonCache.addAll(statuses: newStatuses)
|
||||
self.timelineSegments[0].insertAtBeginning(objects: newStatuses)
|
||||
self.timelineSegments[0].insert(contentsOf: newStatuses.map { ($0.id, .unknown) }, at: 0)
|
||||
|
||||
self.newer = pagination?.newer
|
||||
|
||||
|
@ -231,7 +231,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension ProfileTableViewController: StatusTableViewCellDelegate {
|
||||
func statusCollapsedStateChanged() {
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
// causes the table view to recalculate the cell heights
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
@ -270,7 +270,7 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
|
|||
extension ProfileTableViewController: UITableViewDataSourcePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths where indexPath.section > 1 {
|
||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row]
|
||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row].id
|
||||
guard let status = MastodonCache.status(for: statusID) else { continue }
|
||||
ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments {
|
||||
|
@ -281,7 +281,7 @@ extension ProfileTableViewController: UITableViewDataSourcePrefetching {
|
|||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths where indexPath.section > 1 {
|
||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row]
|
||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row].id
|
||||
guard let status = MastodonCache.status(for: statusID) else { continue }
|
||||
ImageCache.avatars.cancel(status.account.avatar)
|
||||
for attachment in status.attachments {
|
||||
|
|
|
@ -54,9 +54,9 @@ class SearchTableViewController: EnhancedTableViewController {
|
|||
cell.updateUI(hashtag: tag)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
case let .status(id):
|
||||
case let .status(id, state):
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as! TimelineStatusTableViewCell
|
||||
cell.updateUI(statusID: id)
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
}
|
||||
|
@ -113,16 +113,16 @@ class SearchTableViewController: EnhancedTableViewController {
|
|||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
if !results.accounts.isEmpty {
|
||||
snapshot.appendSections([.accounts])
|
||||
snapshot.appendItems(results.accounts.map { Item.account($0.id) }, toSection: .accounts)
|
||||
snapshot.appendItems(results.accounts.map { .account($0.id) }, toSection: .accounts)
|
||||
MastodonCache.addAll(accounts: results.accounts)
|
||||
}
|
||||
if !results.hashtags.isEmpty {
|
||||
snapshot.appendSections([.hashtags])
|
||||
snapshot.appendItems(results.hashtags.map { Item.hashtag($0) }, toSection: .hashtags)
|
||||
snapshot.appendItems(results.hashtags.map { .hashtag($0) }, toSection: .hashtags)
|
||||
}
|
||||
if !results.statuses.isEmpty {
|
||||
snapshot.appendSections([.statuses])
|
||||
snapshot.appendItems(results.statuses.map { Item.status($0.id) }, toSection: .statuses)
|
||||
snapshot.appendItems(results.statuses.map { .status($0.id, .unknown) }, toSection: .statuses)
|
||||
MastodonCache.addAll(statuses: results.statuses)
|
||||
MastodonCache.addAll(accounts: results.statuses.map { $0.account })
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ extension SearchTableViewController {
|
|||
enum Item: Hashable {
|
||||
case account(String)
|
||||
case hashtag(Hashtag)
|
||||
case status(String)
|
||||
case status(String, StatusState)
|
||||
}
|
||||
|
||||
class DataSource: UITableViewDiffableDataSource<Section, Item> {
|
||||
|
@ -176,7 +176,7 @@ extension SearchTableViewController: UISearchBarDelegate {
|
|||
}
|
||||
|
||||
extension SearchTableViewController: StatusTableViewCellDelegate {
|
||||
func statusCollapsedStateChanged() {
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
|||
|
||||
let actionType: ActionType
|
||||
let statusID: String
|
||||
var statusState: StatusState
|
||||
var accountIDs: [String]? {
|
||||
didSet {
|
||||
tableView.reloadData()
|
||||
|
@ -32,9 +33,10 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
|||
- Parameter statusID The ID of the status to show.
|
||||
- Parameter accountIDs The accounts that will be shown. If `nil` is passed, a request will be performed to load the accounts.
|
||||
*/
|
||||
init(actionType: ActionType, statusID: String, accountIDs: [String]?) {
|
||||
init(actionType: ActionType, statusID: String, statusState: StatusState, accountIDs: [String]?) {
|
||||
self.actionType = actionType
|
||||
self.statusID = statusID
|
||||
self.statusState = statusState
|
||||
self.accountIDs = accountIDs
|
||||
|
||||
super.init(style: .grouped)
|
||||
|
@ -109,7 +111,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
|||
switch indexPath.section {
|
||||
case 0:
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
||||
cell.updateUI(statusID: statusID)
|
||||
cell.updateUI(statusID: statusID, state: statusState)
|
||||
cell.delegate = self
|
||||
return cell
|
||||
case 1:
|
||||
|
@ -135,7 +137,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
|
||||
func statusCollapsedStateChanged() {
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
// causes the table view to recalculate the cell heights
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
|
|
@ -26,7 +26,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
|
||||
var timeline: Timeline!
|
||||
|
||||
var timelineSegments: [TimelineSegment<Status>] = [] {
|
||||
var timelineSegments: [[(id: String, state: StatusState)]] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
|
@ -56,7 +56,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
func statusID(for indexPath: IndexPath) -> String {
|
||||
return timelineSegments[indexPath.section][indexPath.row]
|
||||
return timelineSegments[indexPath.section][indexPath.row].id
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
@ -74,7 +74,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
MastodonController.client.run(request) { response in
|
||||
guard case let .success(statuses, pagination) = response else { fatalError() }
|
||||
MastodonCache.addAll(statuses: statuses)
|
||||
self.timelineSegments.insert(TimelineSegment(objects: statuses), at: 0)
|
||||
self.timelineSegments.insert(statuses.map { ($0.id, .unknown) }, at: 0)
|
||||
self.newer = pagination?.newer
|
||||
self.older = pagination?.older
|
||||
}
|
||||
|
@ -94,7 +94,8 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
||||
|
||||
cell.updateUI(statusID: statusID(for: indexPath))
|
||||
let (id, state) = timelineSegments[indexPath.section][indexPath.row]
|
||||
cell.updateUI(statusID: id, state: state)
|
||||
cell.delegate = self
|
||||
|
||||
return cell
|
||||
|
@ -112,7 +113,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||
self.older = pagination?.older
|
||||
MastodonCache.addAll(statuses: newStatuses)
|
||||
self.timelineSegments[self.timelineSegments.count - 1].append(objects: newStatuses)
|
||||
self.timelineSegments[self.timelineSegments.count - 1].append(contentsOf: newStatuses.map { ($0.id, .unknown) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,7 +138,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||
self.newer = pagination?.newer
|
||||
MastodonCache.addAll(statuses: newStatuses)
|
||||
self.timelineSegments[0].insertAtBeginning(objects: newStatuses)
|
||||
self.timelineSegments[0].insert(contentsOf: newStatuses.map { ($0.id, .unknown) }, at: 0)
|
||||
DispatchQueue.main.async {
|
||||
self.refreshControl?.endRefreshing()
|
||||
|
||||
|
@ -154,7 +155,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
||||
func statusCollapsedStateChanged() {
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||
// causes the table view to recalculate the cell heights
|
||||
tableView.beginUpdates()
|
||||
tableView.endUpdates()
|
||||
|
|
|
@ -24,6 +24,8 @@ protocol TuskerNavigationDelegate {
|
|||
|
||||
func selected(status statusID: String)
|
||||
|
||||
func selected(status statusID: String, state: StatusState)
|
||||
|
||||
func compose()
|
||||
|
||||
func reply(to statusID: String)
|
||||
|
@ -46,7 +48,7 @@ protocol TuskerNavigationDelegate {
|
|||
|
||||
func showFollowedByList(accountIDs: [String])
|
||||
|
||||
func statusActionAccountList(action: StatusActionAccountListTableViewController.ActionType, statusID: String, accountIDs: [String]?) -> StatusActionAccountListTableViewController
|
||||
func statusActionAccountList(action: StatusActionAccountListTableViewController.ActionType, statusID: String, statusState state: StatusState, accountIDs: [String]?) -> StatusActionAccountListTableViewController
|
||||
}
|
||||
|
||||
extension TuskerNavigationDelegate where Self: UIViewController {
|
||||
|
@ -96,13 +98,18 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
|||
}
|
||||
|
||||
func selected(status statusID: String) {
|
||||
self.selected(status: statusID, state: .unknown)
|
||||
}
|
||||
|
||||
func selected(status statusID: String, state: StatusState) {
|
||||
// todo: is this necessary? should the conversation main status cell prevent this
|
||||
// don't open if the conversation is the same as the current one
|
||||
if let conversationController = self as? ConversationTableViewController,
|
||||
conversationController.mainStatusID == statusID {
|
||||
return
|
||||
}
|
||||
|
||||
show(ConversationTableViewController(for: statusID), sender: self)
|
||||
show(ConversationTableViewController(for: statusID, state: state), sender: self)
|
||||
}
|
||||
|
||||
func compose() {
|
||||
|
@ -195,8 +202,8 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
|||
show(vc, sender: self)
|
||||
}
|
||||
|
||||
func statusActionAccountList(action: StatusActionAccountListTableViewController.ActionType, statusID: String, accountIDs: [String]?) -> StatusActionAccountListTableViewController {
|
||||
return StatusActionAccountListTableViewController(actionType: action, statusID: statusID, accountIDs: accountIDs)
|
||||
func statusActionAccountList(action: StatusActionAccountListTableViewController.ActionType, statusID: String, statusState state: StatusState, accountIDs: [String]?) -> StatusActionAccountListTableViewController {
|
||||
return StatusActionAccountListTableViewController(actionType: action, statusID: statusID, statusState: state, accountIDs: accountIDs)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
|||
default:
|
||||
fatalError()
|
||||
}
|
||||
let vc = delegate.statusActionAccountList(action: action, statusID: statusID, accountIDs: accountIDs)
|
||||
let vc = delegate.statusActionAccountList(action: action, statusID: statusID, statusState: .unknown, accountIDs: accountIDs)
|
||||
delegate.show(vc)
|
||||
}
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ extension ActionNotificationGroupTableViewCell: MenuPreviewProvider {
|
|||
default:
|
||||
fatalError()
|
||||
}
|
||||
return self.delegate?.statusActionAccountList(action: action, statusID: self.statusID, accountIDs: accountIDs)
|
||||
return self.delegate?.statusActionAccountList(action: action, statusID: self.statusID, statusState: .unknown, accountIDs: accountIDs)
|
||||
}, actions: {
|
||||
return []
|
||||
})
|
||||
|
|
|
@ -11,7 +11,7 @@ import Pachyderm
|
|||
import Combine
|
||||
|
||||
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
|
||||
func statusCollapsedStateChanged()
|
||||
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell)
|
||||
}
|
||||
|
||||
class BaseStatusTableViewCell: UITableViewCell {
|
||||
|
@ -48,12 +48,18 @@ class BaseStatusTableViewCell: UITableViewCell {
|
|||
}
|
||||
}
|
||||
|
||||
var statusState: StatusState!
|
||||
var collapsible = false {
|
||||
didSet {
|
||||
collapseButton.isHidden = !collapsible
|
||||
statusState?.collapsible = collapsible
|
||||
}
|
||||
}
|
||||
var collapsed = false {
|
||||
didSet {
|
||||
statusState?.collapsed = collapsed
|
||||
}
|
||||
}
|
||||
var collapsed = false
|
||||
var showStatusAutomatically = false
|
||||
|
||||
var avatarURL: URL?
|
||||
|
@ -99,11 +105,12 @@ class BaseStatusTableViewCell: UITableViewCell {
|
|||
.sink(receiveValue: updateUI(account:))
|
||||
}
|
||||
|
||||
func updateUI(statusID: String) {
|
||||
func updateUI(statusID: String, state: StatusState) {
|
||||
guard let status = MastodonCache.status(for: statusID) else {
|
||||
fatalError("Missing cached status")
|
||||
}
|
||||
self.statusID = statusID
|
||||
self.statusState = state
|
||||
|
||||
let account = status.account
|
||||
self.accountID = account.id
|
||||
|
@ -119,20 +126,29 @@ class BaseStatusTableViewCell: UITableViewCell {
|
|||
|
||||
contentLabel.statusID = statusID
|
||||
|
||||
collapsible = !status.spoilerText.isEmpty
|
||||
var shouldCollapse = collapsible
|
||||
contentWarningLabel.text = status.spoilerText
|
||||
contentWarningLabel.isHidden = status.spoilerText.isEmpty
|
||||
if !shouldCollapse,
|
||||
let text = contentLabel.text,
|
||||
text.count > 500 {
|
||||
collapsible = true
|
||||
shouldCollapse = true
|
||||
|
||||
if state.unknown {
|
||||
collapsible = !status.spoilerText.isEmpty
|
||||
var shouldCollapse = collapsible
|
||||
if !shouldCollapse,
|
||||
let text = contentLabel.text,
|
||||
text.count > 500 {
|
||||
collapsible = true
|
||||
shouldCollapse = true
|
||||
}
|
||||
if collapsible && showStatusAutomatically {
|
||||
shouldCollapse = false
|
||||
}
|
||||
setCollapsed(shouldCollapse, animated: false)
|
||||
|
||||
state.collapsible = collapsible
|
||||
state.collapsed = shouldCollapse
|
||||
} else {
|
||||
collapsible = state.collapsible!
|
||||
setCollapsed(state.collapsed!, animated: false)
|
||||
}
|
||||
if collapsible && showStatusAutomatically {
|
||||
shouldCollapse = false
|
||||
}
|
||||
setCollapsed(shouldCollapse, animated: false)
|
||||
}
|
||||
|
||||
func updateStatusState(status: Status) {
|
||||
|
@ -182,7 +198,7 @@ class BaseStatusTableViewCell: UITableViewCell {
|
|||
|
||||
@IBAction func collapseButtonPressed() {
|
||||
setCollapsed(!collapsed, animated: true)
|
||||
delegate?.statusCollapsedStateChanged()
|
||||
delegate?.statusCellCollapsedStateChanged(self)
|
||||
}
|
||||
|
||||
func setCollapsed(_ collapsed: Bool, animated: Bool) {
|
||||
|
|
|
@ -36,8 +36,8 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
|||
accessibilityElements = [profileAccessibilityElement!, contentWarningLabel!, collapseButton!, contentLabel!, totalFavoritesButton!, totalReblogsButton!, timestampAndClientLabel!, replyButton!, favoriteButton!, reblogButton!, moreButton!]
|
||||
}
|
||||
|
||||
override func updateUI(statusID: String) {
|
||||
super.updateUI(statusID: statusID)
|
||||
override func updateUI(statusID: String, state: StatusState) {
|
||||
super.updateUI(statusID: statusID, state: state)
|
||||
guard let status = MastodonCache.status(for: statusID) else { fatalError() }
|
||||
|
||||
var timestampAndClientText = ConversationMainStatusTableViewCell.dateFormatter.string(from: status.createdAt)
|
||||
|
@ -70,7 +70,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
|||
@IBAction func totalFavoritesPressed() {
|
||||
if let delegate = delegate {
|
||||
// accounts aren't known, pass nil so the VC will load them
|
||||
let vc = delegate.statusActionAccountList(action: .favorite, statusID: statusID, accountIDs: nil)
|
||||
let vc = delegate.statusActionAccountList(action: .favorite, statusID: statusID, statusState: statusState.copy(), accountIDs: nil)
|
||||
vc.showInacurateCountWarning = true
|
||||
delegate.show(vc)
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
|||
@IBAction func totalReblogsPressed() {
|
||||
if let delegate = delegate {
|
||||
// accounts aren't known, pass nil so the VC will load them
|
||||
let vc = delegate.statusActionAccountList(action: .reblog, statusID: statusID, accountIDs: nil)
|
||||
let vc = delegate.statusActionAccountList(action: .reblog, statusID: statusID, statusState: statusState.copy(), accountIDs: nil)
|
||||
vc.showInacurateCountWarning = true
|
||||
delegate.show(vc)
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
|||
.sink(receiveValue: updateRebloggerLabel(reblogger:))
|
||||
}
|
||||
|
||||
override func updateUI(statusID: String) {
|
||||
override func updateUI(statusID: String, state: StatusState) {
|
||||
guard var status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
|
||||
|
||||
let realStatusID: String
|
||||
|
@ -66,7 +66,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
|||
realStatusID = statusID
|
||||
}
|
||||
|
||||
super.updateUI(statusID: realStatusID)
|
||||
super.updateUI(statusID: realStatusID, state: state)
|
||||
|
||||
updateTimestamp()
|
||||
|
||||
|
@ -123,7 +123,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
|||
super.setSelected(selected, animated: animated)
|
||||
|
||||
if selected {
|
||||
delegate?.selected(status: statusID)
|
||||
delegate?.selected(status: statusID, state: statusState.copy())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
|||
|
||||
override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? {
|
||||
return (
|
||||
content: { ConversationTableViewController(for: self.statusID) },
|
||||
content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy()) },
|
||||
actions: { self.actionsForStatus(statusID: self.statusID) }
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue