forked from shadowfacts/Tusker
Store status collapse state in containing view controller
Also, copy the state between screens, so e.g. expanding a status in the timeline and then opening that conversation already has that status expanded. This intentionally doesn't store the sensitive attachment visibility state, since showing text when not necessary is less dangerous than for images. (Possibly a preference for this in the future?) Closes #55
This commit is contained in:
parent
24a1e7ceb9
commit
b47b08fa95
|
@ -12,12 +12,18 @@ public class NotificationGroup {
|
||||||
public let notificationIDs: [String]
|
public let notificationIDs: [String]
|
||||||
public let id: String
|
public let id: String
|
||||||
public let kind: Notification.Kind
|
public let kind: Notification.Kind
|
||||||
|
public let statusState: StatusState?
|
||||||
|
|
||||||
init?(notifications: [Notification]) {
|
init?(notifications: [Notification]) {
|
||||||
guard !notifications.isEmpty else { return nil }
|
guard !notifications.isEmpty else { return nil }
|
||||||
self.notificationIDs = notifications.map { $0.id }
|
self.notificationIDs = notifications.map { $0.id }
|
||||||
self.id = notifications.first!.id
|
self.id = notifications.first!.id
|
||||||
self.kind = notifications.first!.kind
|
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] {
|
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 */; };
|
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
|
||||||
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.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 */; };
|
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 */; };
|
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
|
||||||
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; };
|
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; };
|
||||||
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
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>"; };
|
D60A548E21ED515800F1F87C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
@ -1000,6 +1002,7 @@
|
||||||
D6A3BC7223218C6E00FD64D5 /* Utilities */ = {
|
D6A3BC7223218C6E00FD64D5 /* Utilities */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D60A4FFB238B726A008AC647 /* StatusState.swift */,
|
||||||
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */,
|
D6E6F26221603F8B006A8599 /* CharacterCounter.swift */,
|
||||||
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */,
|
D6A3BC7323218C6E00FD64D5 /* TimelineSegment.swift */,
|
||||||
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */,
|
D6A3BC7823218E9200FD64D5 /* NotificationGroup.swift */,
|
||||||
|
@ -1534,6 +1537,7 @@
|
||||||
D61099F5214568C300432DC2 /* Notification.swift in Sources */,
|
D61099F5214568C300432DC2 /* Notification.swift in Sources */,
|
||||||
D61099EF214566C000432DC2 /* Instance.swift in Sources */,
|
D61099EF214566C000432DC2 /* Instance.swift in Sources */,
|
||||||
D61099D22144B2E600432DC2 /* Body.swift in Sources */,
|
D61099D22144B2E600432DC2 /* Body.swift in Sources */,
|
||||||
|
D63569E023908A8D003DD353 /* StatusState.swift in Sources */,
|
||||||
D6109A0121456B0800432DC2 /* Hashtag.swift in Sources */,
|
D6109A0121456B0800432DC2 /* Hashtag.swift in Sources */,
|
||||||
D61099FD21456A1D00432DC2 /* SearchResults.swift in Sources */,
|
D61099FD21456A1D00432DC2 /* SearchResults.swift in Sources */,
|
||||||
D61099F12145686D00432DC2 /* List.swift in Sources */,
|
D61099F12145686D00432DC2 /* List.swift in Sources */,
|
||||||
|
|
|
@ -15,8 +15,9 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
static let showPostsImage = UIImage(systemName: "eye.fill")!
|
static let showPostsImage = UIImage(systemName: "eye.fill")!
|
||||||
static let hidePostsImage = UIImage(systemName: "eye.slash.fill")!
|
static let hidePostsImage = UIImage(systemName: "eye.slash.fill")!
|
||||||
|
|
||||||
var mainStatusID: String!
|
let mainStatusID: String
|
||||||
var statusIDs: [String] = [] {
|
let mainStatusState: StatusState
|
||||||
|
var statuses: [(id: String, state: StatusState)] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
|
@ -27,8 +28,9 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
var showStatusesAutomatically = false
|
var showStatusesAutomatically = false
|
||||||
var visibilityBarButtonItem: UIBarButtonItem!
|
var visibilityBarButtonItem: UIBarButtonItem!
|
||||||
|
|
||||||
init(for mainStatusID: String) {
|
init(for mainStatusID: String, state: StatusState = .unknown) {
|
||||||
self.mainStatusID = mainStatusID
|
self.mainStatusID = mainStatusID
|
||||||
|
self.mainStatusState = state
|
||||||
|
|
||||||
super.init(style: .plain)
|
super.init(style: .plain)
|
||||||
}
|
}
|
||||||
|
@ -51,9 +53,9 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
visibilityBarButtonItem = UIBarButtonItem(image: ConversationTableViewController.showPostsImage, style: .plain, target: self, action: #selector(toggleVisibilityButtonPressed))
|
visibilityBarButtonItem = UIBarButtonItem(image: ConversationTableViewController.showPostsImage, style: .plain, target: self, action: #selector(toggleVisibilityButtonPressed))
|
||||||
navigationItem.rightBarButtonItem = visibilityBarButtonItem
|
navigationItem.rightBarButtonItem = visibilityBarButtonItem
|
||||||
|
|
||||||
statusIDs = [mainStatusID]
|
statuses = [(mainStatusID, mainStatusState)]
|
||||||
|
|
||||||
guard let mainStatus = MastodonCache.status(for: mainStatusID) else { fatalError("Missing cached status \(mainStatusID!)") }
|
guard let mainStatus = MastodonCache.status(for: mainStatusID) else { fatalError("Missing cached status \(mainStatusID)") }
|
||||||
|
|
||||||
let request = Status.getContext(mainStatus)
|
let request = Status.getContext(mainStatus)
|
||||||
MastodonController.client.run(request) { response in
|
MastodonController.client.run(request) { response in
|
||||||
|
@ -61,8 +63,8 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
let parents = self.getDirectParents(of: mainStatus, from: context.ancestors)
|
let parents = self.getDirectParents(of: mainStatus, from: context.ancestors)
|
||||||
MastodonCache.addAll(statuses: parents)
|
MastodonCache.addAll(statuses: parents)
|
||||||
MastodonCache.addAll(statuses: context.descendants)
|
MastodonCache.addAll(statuses: context.descendants)
|
||||||
self.statusIDs = parents.map { $0.id } + [self.mainStatusID] + context.descendants.map { $0.id }
|
self.statuses = parents.map { ($0.id, .unknown) } + self.statuses + context.descendants.map { ($0.id, .unknown) }
|
||||||
let indexPath = IndexPath(row: self.statusIDs.firstIndex(of: self.mainStatusID)!, section: 0)
|
let indexPath = IndexPath(row: parents.count, section: 0)
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.scrollToRow(at: indexPath, at: .middle, animated: false)
|
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 {
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||||
return statusIDs.count
|
return statuses.count
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
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() }
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: "mainStatusCell", for: indexPath) as? ConversationMainStatusTableViewCell else { fatalError() }
|
||||||
cell.selectionStyle = .none
|
cell.selectionStyle = .none
|
||||||
cell.showStatusAutomatically = showStatusesAutomatically
|
cell.showStatusAutomatically = showStatusesAutomatically
|
||||||
cell.updateUI(statusID: statusID)
|
cell.updateUI(statusID: id, state: state)
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
} else {
|
} else {
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
||||||
cell.showStatusAutomatically = showStatusesAutomatically
|
cell.showStatusAutomatically = showStatusesAutomatically
|
||||||
cell.updateUI(statusID: statusID)
|
cell.updateUI(statusID: id, state: state)
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
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
|
return statusID == mainStatusID ? nil : indexPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,9 +138,14 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
cell.collapsible else { continue }
|
cell.collapsible else { continue }
|
||||||
cell.showStatusAutomatically = showStatusesAutomatically
|
cell.showStatusAutomatically = showStatusesAutomatically
|
||||||
cell.setCollapsed(!showStatusesAutomatically, animated: false)
|
cell.setCollapsed(!showStatusesAutomatically, animated: false)
|
||||||
|
let indexPath = tableView.indexPath(for: cell)!
|
||||||
|
statuses[indexPath.row].state.collapsed = !showStatusesAutomatically
|
||||||
}
|
}
|
||||||
statusCollapsedStateChanged()
|
|
||||||
|
|
||||||
|
// recalculate cell heights
|
||||||
|
tableView.beginUpdates()
|
||||||
|
tableView.endUpdates()
|
||||||
|
|
||||||
if showStatusesAutomatically {
|
if showStatusesAutomatically {
|
||||||
visibilityBarButtonItem.image = ConversationTableViewController.hidePostsImage
|
visibilityBarButtonItem.image = ConversationTableViewController.hidePostsImage
|
||||||
} else {
|
} else {
|
||||||
|
@ -149,7 +156,7 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ConversationTableViewController: StatusTableViewCellDelegate {
|
extension ConversationTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCollapsedStateChanged() {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
// causes the table view to recalculate the cell heights
|
// causes the table view to recalculate the cell heights
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
tableView.endUpdates()
|
tableView.endUpdates()
|
||||||
|
@ -159,7 +166,7 @@ extension ConversationTableViewController: StatusTableViewCellDelegate {
|
||||||
extension ConversationTableViewController: UITableViewDataSourcePrefetching {
|
extension ConversationTableViewController: UITableViewDataSourcePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
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)
|
ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||||
for attachment in status.attachments {
|
for attachment in status.attachments {
|
||||||
ImageCache.attachments.get(attachment.url, completion: nil)
|
ImageCache.attachments.get(attachment.url, completion: nil)
|
||||||
|
@ -169,7 +176,7 @@ extension ConversationTableViewController: UITableViewDataSourcePrefetching {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths {
|
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)
|
ImageCache.avatars.cancel(status.account.avatar)
|
||||||
for attachment in status.attachments {
|
for attachment in status.attachments {
|
||||||
ImageCache.attachments.cancel(attachment.url)
|
ImageCache.attachments.cancel(attachment.url)
|
||||||
|
|
|
@ -91,7 +91,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as? TimelineStatusTableViewCell else {
|
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as? TimelineStatusTableViewCell else {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
cell.updateUI(statusID: notification.status!.id)
|
cell.updateUI(statusID: notification.status!.id, state: group.statusState!)
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationsTableViewController: StatusTableViewCellDelegate {
|
extension NotificationsTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCollapsedStateChanged() {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
// causes the table view to recalculate the cell heights
|
// causes the table view to recalculate the cell heights
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
tableView.endUpdates()
|
tableView.endUpdates()
|
||||||
|
|
|
@ -22,14 +22,14 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var pinnedStatusIDs: [String] = [] {
|
var pinnedStatuses: [(id: String, state: StatusState)] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var timelineSegments: [TimelineSegment<Status>] = [] {
|
var timelineSegments: [[(id: String, state: StatusState)]] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
|
@ -109,14 +109,14 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
guard case let .success(statuses, _) = response else { fatalError() }
|
guard case let .success(statuses, _) = response else { fatalError() }
|
||||||
|
|
||||||
MastodonCache.addAll(statuses: statuses)
|
MastodonCache.addAll(statuses: statuses)
|
||||||
self.pinnedStatusIDs = statuses.map { $0.id }
|
self.pinnedStatuses = statuses.map { ($0.id, .unknown) }
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatuses() { response in
|
getStatuses() { response in
|
||||||
guard case let .success(statuses, pagination) = response else { fatalError() }
|
guard case let .success(statuses, pagination) = response else { fatalError() }
|
||||||
|
|
||||||
MastodonCache.addAll(statuses: statuses)
|
MastodonCache.addAll(statuses: statuses)
|
||||||
self.timelineSegments.append(TimelineSegment(objects: statuses))
|
self.timelineSegments.append(statuses.map { ($0.id, .unknown) })
|
||||||
|
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
self.newer = pagination?.newer
|
self.newer = pagination?.newer
|
||||||
|
@ -150,7 +150,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
if section == 0 {
|
if section == 0 {
|
||||||
return accountID == nil || MastodonCache.account(for: accountID) == nil ? 0 : 1
|
return accountID == nil || MastodonCache.account(for: accountID) == nil ? 0 : 1
|
||||||
} else if section == 1 {
|
} else if section == 1 {
|
||||||
return pinnedStatusIDs.count
|
return pinnedStatuses.count
|
||||||
} else {
|
} else {
|
||||||
return timelineSegments[section - 2].count
|
return timelineSegments[section - 2].count
|
||||||
}
|
}
|
||||||
|
@ -166,15 +166,15 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
return cell
|
return cell
|
||||||
case 1:
|
case 1:
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
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.showPinned = true
|
||||||
cell.updateUI(statusID: statusID)
|
cell.updateUI(statusID: id, state: state)
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
default:
|
default:
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
||||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row]
|
let (id, state) = timelineSegments[indexPath.section - 2][indexPath.row]
|
||||||
cell.updateUI(statusID: statusID)
|
cell.updateUI(statusID: id, state: state)
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
@ -188,7 +188,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||||
|
|
||||||
MastodonCache.addAll(statuses: newStatuses)
|
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
|
self.older = pagination?.older
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||||
|
|
||||||
MastodonCache.addAll(statuses: newStatuses)
|
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
|
self.newer = pagination?.newer
|
||||||
|
|
||||||
|
@ -231,7 +231,7 @@ class ProfileTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ProfileTableViewController: StatusTableViewCellDelegate {
|
extension ProfileTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCollapsedStateChanged() {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
// causes the table view to recalculate the cell heights
|
// causes the table view to recalculate the cell heights
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
tableView.endUpdates()
|
tableView.endUpdates()
|
||||||
|
@ -270,7 +270,7 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
|
||||||
extension ProfileTableViewController: UITableViewDataSourcePrefetching {
|
extension ProfileTableViewController: UITableViewDataSourcePrefetching {
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths where indexPath.section > 1 {
|
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 }
|
guard let status = MastodonCache.status(for: statusID) else { continue }
|
||||||
ImageCache.avatars.get(status.account.avatar, completion: nil)
|
ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||||
for attachment in status.attachments {
|
for attachment in status.attachments {
|
||||||
|
@ -281,7 +281,7 @@ extension ProfileTableViewController: UITableViewDataSourcePrefetching {
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||||
for indexPath in indexPaths where indexPath.section > 1 {
|
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 }
|
guard let status = MastodonCache.status(for: statusID) else { continue }
|
||||||
ImageCache.avatars.cancel(status.account.avatar)
|
ImageCache.avatars.cancel(status.account.avatar)
|
||||||
for attachment in status.attachments {
|
for attachment in status.attachments {
|
||||||
|
|
|
@ -54,9 +54,9 @@ class SearchTableViewController: EnhancedTableViewController {
|
||||||
cell.updateUI(hashtag: tag)
|
cell.updateUI(hashtag: tag)
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
case let .status(id):
|
case let .status(id, state):
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as! TimelineStatusTableViewCell
|
let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as! TimelineStatusTableViewCell
|
||||||
cell.updateUI(statusID: id)
|
cell.updateUI(statusID: id, state: state)
|
||||||
cell.delegate = self
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
@ -113,16 +113,16 @@ class SearchTableViewController: EnhancedTableViewController {
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||||
if !results.accounts.isEmpty {
|
if !results.accounts.isEmpty {
|
||||||
snapshot.appendSections([.accounts])
|
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)
|
MastodonCache.addAll(accounts: results.accounts)
|
||||||
}
|
}
|
||||||
if !results.hashtags.isEmpty {
|
if !results.hashtags.isEmpty {
|
||||||
snapshot.appendSections([.hashtags])
|
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 {
|
if !results.statuses.isEmpty {
|
||||||
snapshot.appendSections([.statuses])
|
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(statuses: results.statuses)
|
||||||
MastodonCache.addAll(accounts: results.statuses.map { $0.account })
|
MastodonCache.addAll(accounts: results.statuses.map { $0.account })
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ extension SearchTableViewController {
|
||||||
enum Item: Hashable {
|
enum Item: Hashable {
|
||||||
case account(String)
|
case account(String)
|
||||||
case hashtag(Hashtag)
|
case hashtag(Hashtag)
|
||||||
case status(String)
|
case status(String, StatusState)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataSource: UITableViewDiffableDataSource<Section, Item> {
|
class DataSource: UITableViewDiffableDataSource<Section, Item> {
|
||||||
|
@ -176,7 +176,7 @@ extension SearchTableViewController: UISearchBarDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SearchTableViewController: StatusTableViewCellDelegate {
|
extension SearchTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCollapsedStateChanged() {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
tableView.endUpdates()
|
tableView.endUpdates()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
let actionType: ActionType
|
let actionType: ActionType
|
||||||
let statusID: String
|
let statusID: String
|
||||||
|
var statusState: StatusState
|
||||||
var accountIDs: [String]? {
|
var accountIDs: [String]? {
|
||||||
didSet {
|
didSet {
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
|
@ -32,9 +33,10 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
- Parameter statusID The ID of the status to show.
|
- 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.
|
- 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.actionType = actionType
|
||||||
self.statusID = statusID
|
self.statusID = statusID
|
||||||
|
self.statusState = statusState
|
||||||
self.accountIDs = accountIDs
|
self.accountIDs = accountIDs
|
||||||
|
|
||||||
super.init(style: .grouped)
|
super.init(style: .grouped)
|
||||||
|
@ -109,7 +111,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
switch indexPath.section {
|
switch indexPath.section {
|
||||||
case 0:
|
case 0:
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: statusCell, for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
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
|
cell.delegate = self
|
||||||
return cell
|
return cell
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -135,7 +137,7 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
|
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCollapsedStateChanged() {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
// causes the table view to recalculate the cell heights
|
// causes the table view to recalculate the cell heights
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
tableView.endUpdates()
|
tableView.endUpdates()
|
||||||
|
|
|
@ -26,7 +26,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
var timeline: Timeline!
|
var timeline: Timeline!
|
||||||
|
|
||||||
var timelineSegments: [TimelineSegment<Status>] = [] {
|
var timelineSegments: [[(id: String, state: StatusState)]] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
|
@ -56,7 +56,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func statusID(for indexPath: IndexPath) -> String {
|
func statusID(for indexPath: IndexPath) -> String {
|
||||||
return timelineSegments[indexPath.section][indexPath.row]
|
return timelineSegments[indexPath.section][indexPath.row].id
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
@ -74,7 +74,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
MastodonController.client.run(request) { response in
|
MastodonController.client.run(request) { response in
|
||||||
guard case let .success(statuses, pagination) = response else { fatalError() }
|
guard case let .success(statuses, pagination) = response else { fatalError() }
|
||||||
MastodonCache.addAll(statuses: statuses)
|
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.newer = pagination?.newer
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,8 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { fatalError() }
|
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
|
cell.delegate = self
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
|
@ -112,7 +113,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||||
self.older = pagination?.older
|
self.older = pagination?.older
|
||||||
MastodonCache.addAll(statuses: newStatuses)
|
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() }
|
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||||
self.newer = pagination?.newer
|
self.newer = pagination?.newer
|
||||||
MastodonCache.addAll(statuses: newStatuses)
|
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 {
|
DispatchQueue.main.async {
|
||||||
self.refreshControl?.endRefreshing()
|
self.refreshControl?.endRefreshing()
|
||||||
|
|
||||||
|
@ -154,7 +155,7 @@ class TimelineTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
extension TimelineTableViewController: StatusTableViewCellDelegate {
|
||||||
func statusCollapsedStateChanged() {
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
|
||||||
// causes the table view to recalculate the cell heights
|
// causes the table view to recalculate the cell heights
|
||||||
tableView.beginUpdates()
|
tableView.beginUpdates()
|
||||||
tableView.endUpdates()
|
tableView.endUpdates()
|
||||||
|
|
|
@ -24,6 +24,8 @@ protocol TuskerNavigationDelegate {
|
||||||
|
|
||||||
func selected(status statusID: String)
|
func selected(status statusID: String)
|
||||||
|
|
||||||
|
func selected(status statusID: String, state: StatusState)
|
||||||
|
|
||||||
func compose()
|
func compose()
|
||||||
|
|
||||||
func reply(to statusID: String)
|
func reply(to statusID: String)
|
||||||
|
@ -46,7 +48,7 @@ protocol TuskerNavigationDelegate {
|
||||||
|
|
||||||
func showFollowedByList(accountIDs: [String])
|
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 {
|
extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
|
@ -96,13 +98,18 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func selected(status statusID: String) {
|
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
|
// don't open if the conversation is the same as the current one
|
||||||
if let conversationController = self as? ConversationTableViewController,
|
if let conversationController = self as? ConversationTableViewController,
|
||||||
conversationController.mainStatusID == statusID {
|
conversationController.mainStatusID == statusID {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
show(ConversationTableViewController(for: statusID), sender: self)
|
show(ConversationTableViewController(for: statusID, state: state), sender: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compose() {
|
func compose() {
|
||||||
|
@ -195,8 +202,8 @@ extension TuskerNavigationDelegate where Self: UIViewController {
|
||||||
show(vc, sender: self)
|
show(vc, sender: self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func statusActionAccountList(action: StatusActionAccountListTableViewController.ActionType, statusID: String, accountIDs: [String]?) -> StatusActionAccountListTableViewController {
|
func statusActionAccountList(action: StatusActionAccountListTableViewController.ActionType, statusID: String, statusState state: StatusState, accountIDs: [String]?) -> StatusActionAccountListTableViewController {
|
||||||
return StatusActionAccountListTableViewController(actionType: action, statusID: statusID, accountIDs: accountIDs)
|
return StatusActionAccountListTableViewController(actionType: action, statusID: statusID, statusState: state, accountIDs: accountIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@ class ActionNotificationGroupTableViewCell: UITableViewCell {
|
||||||
default:
|
default:
|
||||||
fatalError()
|
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)
|
delegate.show(vc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ extension ActionNotificationGroupTableViewCell: MenuPreviewProvider {
|
||||||
default:
|
default:
|
||||||
fatalError()
|
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: {
|
}, actions: {
|
||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|
|
@ -11,7 +11,7 @@ import Pachyderm
|
||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
|
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
|
||||||
func statusCollapsedStateChanged()
|
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell)
|
||||||
}
|
}
|
||||||
|
|
||||||
class BaseStatusTableViewCell: UITableViewCell {
|
class BaseStatusTableViewCell: UITableViewCell {
|
||||||
|
@ -48,12 +48,18 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var statusState: StatusState!
|
||||||
var collapsible = false {
|
var collapsible = false {
|
||||||
didSet {
|
didSet {
|
||||||
collapseButton.isHidden = !collapsible
|
collapseButton.isHidden = !collapsible
|
||||||
|
statusState?.collapsible = collapsible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var collapsed = false {
|
||||||
|
didSet {
|
||||||
|
statusState?.collapsed = collapsed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var collapsed = false
|
|
||||||
var showStatusAutomatically = false
|
var showStatusAutomatically = false
|
||||||
|
|
||||||
var avatarURL: URL?
|
var avatarURL: URL?
|
||||||
|
@ -99,11 +105,12 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
.sink(receiveValue: updateUI(account:))
|
.sink(receiveValue: updateUI(account:))
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUI(statusID: String) {
|
func updateUI(statusID: String, state: StatusState) {
|
||||||
guard let status = MastodonCache.status(for: statusID) else {
|
guard let status = MastodonCache.status(for: statusID) else {
|
||||||
fatalError("Missing cached status")
|
fatalError("Missing cached status")
|
||||||
}
|
}
|
||||||
self.statusID = statusID
|
self.statusID = statusID
|
||||||
|
self.statusState = state
|
||||||
|
|
||||||
let account = status.account
|
let account = status.account
|
||||||
self.accountID = account.id
|
self.accountID = account.id
|
||||||
|
@ -119,20 +126,28 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
contentLabel.statusID = statusID
|
contentLabel.statusID = statusID
|
||||||
|
|
||||||
collapsible = !status.spoilerText.isEmpty
|
if state.unknown {
|
||||||
var shouldCollapse = collapsible
|
collapsible = !status.spoilerText.isEmpty
|
||||||
contentWarningLabel.text = status.spoilerText
|
var shouldCollapse = collapsible
|
||||||
contentWarningLabel.isHidden = status.spoilerText.isEmpty
|
contentWarningLabel.text = status.spoilerText
|
||||||
if !shouldCollapse,
|
contentWarningLabel.isHidden = status.spoilerText.isEmpty
|
||||||
let text = contentLabel.text,
|
if !shouldCollapse,
|
||||||
text.count > 500 {
|
let text = contentLabel.text,
|
||||||
collapsible = true
|
text.count > 500 {
|
||||||
shouldCollapse = true
|
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) {
|
func updateStatusState(status: Status) {
|
||||||
|
@ -182,7 +197,7 @@ class BaseStatusTableViewCell: UITableViewCell {
|
||||||
|
|
||||||
@IBAction func collapseButtonPressed() {
|
@IBAction func collapseButtonPressed() {
|
||||||
setCollapsed(!collapsed, animated: true)
|
setCollapsed(!collapsed, animated: true)
|
||||||
delegate?.statusCollapsedStateChanged()
|
delegate?.statusCellCollapsedStateChanged(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setCollapsed(_ collapsed: Bool, animated: Bool) {
|
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!]
|
accessibilityElements = [profileAccessibilityElement!, contentWarningLabel!, collapseButton!, contentLabel!, totalFavoritesButton!, totalReblogsButton!, timestampAndClientLabel!, replyButton!, favoriteButton!, reblogButton!, moreButton!]
|
||||||
}
|
}
|
||||||
|
|
||||||
override func updateUI(statusID: String) {
|
override func updateUI(statusID: String, state: StatusState) {
|
||||||
super.updateUI(statusID: statusID)
|
super.updateUI(statusID: statusID, state: state)
|
||||||
guard let status = MastodonCache.status(for: statusID) else { fatalError() }
|
guard let status = MastodonCache.status(for: statusID) else { fatalError() }
|
||||||
|
|
||||||
var timestampAndClientText = ConversationMainStatusTableViewCell.dateFormatter.string(from: status.createdAt)
|
var timestampAndClientText = ConversationMainStatusTableViewCell.dateFormatter.string(from: status.createdAt)
|
||||||
|
@ -70,7 +70,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
@IBAction func totalFavoritesPressed() {
|
@IBAction func totalFavoritesPressed() {
|
||||||
if let delegate = delegate {
|
if let delegate = delegate {
|
||||||
// accounts aren't known, pass nil so the VC will load them
|
// 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
|
vc.showInacurateCountWarning = true
|
||||||
delegate.show(vc)
|
delegate.show(vc)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
@IBAction func totalReblogsPressed() {
|
@IBAction func totalReblogsPressed() {
|
||||||
if let delegate = delegate {
|
if let delegate = delegate {
|
||||||
// accounts aren't known, pass nil so the VC will load them
|
// 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
|
vc.showInacurateCountWarning = true
|
||||||
delegate.show(vc)
|
delegate.show(vc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
.sink(receiveValue: updateRebloggerLabel(reblogger:))
|
.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)") }
|
guard var status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
|
||||||
|
|
||||||
let realStatusID: String
|
let realStatusID: String
|
||||||
|
@ -66,7 +66,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
realStatusID = statusID
|
realStatusID = statusID
|
||||||
}
|
}
|
||||||
|
|
||||||
super.updateUI(statusID: realStatusID)
|
super.updateUI(statusID: realStatusID, state: state)
|
||||||
|
|
||||||
updateTimestamp()
|
updateTimestamp()
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
|
||||||
super.setSelected(selected, animated: animated)
|
super.setSelected(selected, animated: animated)
|
||||||
|
|
||||||
if selected {
|
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? {
|
override func getStatusCellPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? {
|
||||||
return (
|
return (
|
||||||
content: { ConversationTableViewController(for: self.statusID) },
|
content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy()) },
|
||||||
actions: { self.actionsForStatus(statusID: self.statusID) }
|
actions: { self.actionsForStatus(statusID: self.statusID) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue