diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 7df6bae54f..29ed6f730c 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ D60A548F21ED515800F1F87C /* GMImagePicker.h in Headers */ = {isa = PBXBuildFile; fileRef = D60A548D21ED515800F1F87C /* GMImagePicker.h */; settings = {ATTRIBUTES = (Public, ); }; }; D60A549221ED515800F1F87C /* GMImagePicker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D60A548B21ED515800F1F87C /* GMImagePicker.framework */; }; D60A549321ED515800F1F87C /* GMImagePicker.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = D60A548B21ED515800F1F87C /* GMImagePicker.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + D60BAFB82383921D00EED893 /* StatusCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60BAFB72383921D00EED893 /* StatusCell.swift */; }; D60C07E421E8176B0057FAA8 /* ComposeMediaView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D60C07E321E8176B0057FAA8 /* ComposeMediaView.xib */; }; D61099B42144B0CC00432DC2 /* Pachyderm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */; }; D61099BB2144B0CC00432DC2 /* PachydermTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61099BA2144B0CC00432DC2 /* PachydermTests.swift */; }; @@ -287,6 +288,7 @@ 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 = ""; }; D60A548E21ED515800F1F87C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D60BAFB72383921D00EED893 /* StatusCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusCell.swift; sourceTree = ""; }; D60C07E321E8176B0057FAA8 /* ComposeMediaView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ComposeMediaView.xib; sourceTree = ""; }; D61099AB2144B0CC00432DC2 /* Pachyderm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pachyderm.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D61099AD2144B0CC00432DC2 /* Pachyderm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Pachyderm.h; sourceTree = ""; }; @@ -831,6 +833,7 @@ D6BED173212667E900F02DA0 /* StatusTableViewCell.swift */, D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */, D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */, + D60BAFB72383921D00EED893 /* StatusCell.swift */, ); path = Status; sourceTree = ""; @@ -1597,6 +1600,7 @@ D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */, D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */, D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */, + D60BAFB82383921D00EED893 /* StatusCell.swift in Sources */, D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */, D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */, D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */, diff --git a/Tusker/Screens/Conversation/ConversationTableViewController.swift b/Tusker/Screens/Conversation/ConversationTableViewController.swift index 9b7117fe44..99b79fb60c 100644 --- a/Tusker/Screens/Conversation/ConversationTableViewController.swift +++ b/Tusker/Screens/Conversation/ConversationTableViewController.swift @@ -12,6 +12,9 @@ import Pachyderm class ConversationTableViewController: EnhancedTableViewController { + static let showPostsImage = UIImage(systemName: "eye.fill")! + static let hidePostsImage = UIImage(systemName: "eye.slash.fill")! + var mainStatusID: String! var statusIDs: [String] = [] { didSet { @@ -21,6 +24,9 @@ class ConversationTableViewController: EnhancedTableViewController { } } + var showStatusesAutomatically = false + var visibilityBarButtonItem: UIBarButtonItem! + init(for mainStatusID: String) { self.mainStatusID = mainStatusID @@ -42,6 +48,9 @@ class ConversationTableViewController: EnhancedTableViewController { tableView.prefetchDataSource = self + 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!)") } @@ -89,11 +98,13 @@ class ConversationTableViewController: EnhancedTableViewController { if statusID == 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.delegate = self return cell } else { guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() } + cell.showStatusAutomatically = showStatusesAutomatically cell.updateUI(statusID: statusID) cell.delegate = self return cell @@ -116,6 +127,24 @@ class ConversationTableViewController: EnhancedTableViewController { override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { return (tableView.cellForRow(at: indexPath) as? TableViewSwipeActionProvider)?.trailingSwipeActionsConfiguration() } + + @objc func toggleVisibilityButtonPressed() { + showStatusesAutomatically = !showStatusesAutomatically + + for cell in tableView.visibleCells { + guard var cell = cell as? UITableViewCell & StatusCell, + cell.collapsible else { continue } + cell.showStatusAutomatically = showStatusesAutomatically + cell.setCollapsed(!showStatusesAutomatically, animated: false) + } + statusCollapsedStateChanged() + + if showStatusesAutomatically { + visibilityBarButtonItem.image = ConversationTableViewController.hidePostsImage + } else { + visibilityBarButtonItem.image = ConversationTableViewController.showPostsImage + } + } } diff --git a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift index 3f1a0bd481..80ed1e9a5d 100644 --- a/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift +++ b/Tusker/Views/Status/ConversationMainStatusTableViewCell.swift @@ -10,7 +10,7 @@ import UIKit import Combine import Pachyderm -class ConversationMainStatusTableViewCell: UITableViewCell { +class ConversationMainStatusTableViewCell: UITableViewCell, StatusCell { static let dateFormatter: DateFormatter = { let formatter = DateFormatter() @@ -69,6 +69,8 @@ class ConversationMainStatusTableViewCell: UITableViewCell { } var collapsed = false + var showStatusAutomatically = false + var avatarURL: URL? var statusUpdater: Cancellable? @@ -136,9 +138,15 @@ class ConversationMainStatusTableViewCell: UITableViewCell { contentLabel.statusID = statusID collapsible = !status.spoilerText.isEmpty - setCollapsed(collapsible, animated: false) + var shouldCollapse = collapsible contentWarningLabel.text = status.spoilerText contentWarningLabel.isHidden = status.spoilerText.isEmpty + + if collapsible && showStatusAutomatically { + shouldCollapse = false + } + + setCollapsed(shouldCollapse, animated: false) } private func updateStatusState(status: Status) { @@ -189,6 +197,7 @@ class ConversationMainStatusTableViewCell: UITableViewCell { ImageCache.avatars.cancel(url) } attachmentsView.subviews.forEach { $0.removeFromSuperview() } + showStatusAutomatically = false } @objc func accountPressed() { diff --git a/Tusker/Views/Status/StatusCell.swift b/Tusker/Views/Status/StatusCell.swift new file mode 100644 index 0000000000..7712f32391 --- /dev/null +++ b/Tusker/Views/Status/StatusCell.swift @@ -0,0 +1,16 @@ +// +// StatusCell.swift +// Tusker +// +// Created by Shadowfacts on 11/18/19. +// Copyright © 2019 Shadowfacts. All rights reserved. +// + +import UIKit + +protocol StatusCell { + var collapsible: Bool { get } + var showStatusAutomatically: Bool { get set } + + func setCollapsed(_ collapsed: Bool, animated: Bool) +} diff --git a/Tusker/Views/Status/StatusTableViewCell.swift b/Tusker/Views/Status/StatusTableViewCell.swift index 5f971bb935..b5b6ffab5b 100644 --- a/Tusker/Views/Status/StatusTableViewCell.swift +++ b/Tusker/Views/Status/StatusTableViewCell.swift @@ -14,7 +14,7 @@ protocol StatusTableViewCellDelegate: TuskerNavigationDelegate { func statusCollapsedStateChanged() } -class StatusTableViewCell: UITableViewCell { +class StatusTableViewCell: UITableViewCell, StatusCell { static let relativeDateFormatter: RelativeDateTimeFormatter = { let formatter = RelativeDateTimeFormatter() @@ -68,6 +68,8 @@ class StatusTableViewCell: UITableViewCell { } var collapsed = false + var showStatusAutomatically = false + var avatarURL: URL? var updateTimestampWorkItem: DispatchWorkItem? var attachmentDataTasks: [URLSessionDataTask] = [] @@ -149,17 +151,23 @@ class StatusTableViewCell: UITableViewCell { contentLabel.statusID = status.id collapsible = !status.spoilerText.isEmpty - setCollapsed(collapsible, animated: false) + var shouldCollapse = collapsible contentWarningLabel.text = status.spoilerText contentWarningLabel.isHidden = status.spoilerText.isEmpty - if !collapsed, + if !shouldCollapse, let text = contentLabel.text, text.count > 500 { collapsible = true - setCollapsed(true, animated: false) + shouldCollapse = true } + if collapsible && showStatusAutomatically { + shouldCollapse = false + } + + setCollapsed(shouldCollapse, animated: false) + let pinned = status.pinned ?? false pinImageView.isHidden = !(pinned && showPinned) timestampLabel.isHidden = !pinImageView.isHidden @@ -240,6 +248,7 @@ class StatusTableViewCell: UITableViewCell { updateTimestampWorkItem?.cancel() updateTimestampWorkItem = nil attachmentsView.attachmentViews.allObjects.forEach { $0.removeFromSuperview() } + showStatusAutomatically = false showPinned = false }