Indicate when a followed hashtag caused a post to appear in the home timeline

This commit is contained in:
Shadowfacts 2022-11-29 21:49:47 -05:00
parent 97d5b955a0
commit c6da754875
2 changed files with 48 additions and 24 deletions

View File

@ -101,6 +101,11 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
// separate method because InstanceTimelineViewController needs to be able to customize it // separate method because InstanceTimelineViewController needs to be able to customize it
func configureStatusCell(_ cell: TimelineStatusCollectionViewCell, id: String, state: StatusState) { func configureStatusCell(_ cell: TimelineStatusCollectionViewCell, id: String, state: StatusState) {
cell.delegate = self cell.delegate = self
if case .home = timeline {
cell.showFollowedHashtags = true
} else {
cell.showFollowedHashtags = false
}
cell.updateUI(statusID: id, state: state) cell.updateUI(statusID: id, state: state)
} }

View File

@ -10,23 +10,26 @@ import UIKit
import Pachyderm import Pachyderm
import Combine import Combine
private let reblogIcon = UIImage(systemName: "repeat")
private let hashtagIcon = UIImage(systemName: "number")
class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollectionViewCell { class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollectionViewCell {
static let separatorInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 0) static let separatorInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 0)
// MARK: Subviews // MARK: Subviews
private lazy var rebloggerLabel = EmojiLabel().configure { private lazy var timelineReasonLabel = EmojiLabel().configure {
$0.textColor = .secondaryLabel $0.textColor = .secondaryLabel
$0.font = .preferredFont(forTextStyle: .body) $0.font = .preferredFont(forTextStyle: .body)
$0.adjustsFontForContentSizeCategory = true $0.adjustsFontForContentSizeCategory = true
} }
private let reblogIcon = UIImageView(image: UIImage(systemName: "repeat")).configure { private let timelineReasonIcon = UIImageView(image: reblogIcon).configure {
$0.tintColor = .secondaryLabel $0.tintColor = .secondaryLabel
} }
private lazy var reblogHStack = UIStackView(arrangedSubviews: [ private lazy var timelineReasonHStack = UIStackView(arrangedSubviews: [
reblogIcon, timelineReasonIcon,
rebloggerLabel, timelineReasonLabel,
]).configure { ]).configure {
$0.axis = .horizontal $0.axis = .horizontal
$0.spacing = 8 $0.spacing = 8
@ -258,6 +261,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
true true
} }
var showPinned: Bool = false var showPinned: Bool = false
var showFollowedHashtags: Bool = false
// alas these need to be internal so they're accessible from the protocol extensions // alas these need to be internal so they're accessible from the protocol extensions
var statusID: String! var statusID: String!
@ -275,12 +279,12 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
override init(frame: CGRect) { override init(frame: CGRect) {
super.init(frame: frame) super.init(frame: frame)
for subview in [reblogHStack, mainContainer, actionsContainer] { for subview in [timelineReasonHStack, mainContainer, actionsContainer] {
subview.translatesAutoresizingMaskIntoConstraints = false subview.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(subview) contentView.addSubview(subview)
} }
mainContainerTopToReblogLabelConstraint = mainContainer.topAnchor.constraint(equalTo: reblogHStack.bottomAnchor, constant: 4) mainContainerTopToReblogLabelConstraint = mainContainer.topAnchor.constraint(equalTo: timelineReasonHStack.bottomAnchor, constant: 4)
mainContainerTopToSelfConstraint = mainContainer.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8) mainContainerTopToSelfConstraint = mainContainer.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8)
mainContainerBottomToActionsConstraint = mainContainer.bottomAnchor.constraint(equalTo: actionsContainer.topAnchor, constant: -4) mainContainerBottomToActionsConstraint = mainContainer.bottomAnchor.constraint(equalTo: actionsContainer.topAnchor, constant: -4)
mainContainerBottomToSelfConstraint = mainContainer.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -6) mainContainerBottomToSelfConstraint = mainContainer.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -6)
@ -291,9 +295,9 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
// why is this 4 but the mainContainerTopSelfConstraint constant 8? because this looks more balanced // why is this 4 but the mainContainerTopSelfConstraint constant 8? because this looks more balanced
reblogHStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4), timelineReasonHStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 4),
rebloggerLabel.leadingAnchor.constraint(equalTo: contentVStack.leadingAnchor), timelineReasonLabel.leadingAnchor.constraint(equalTo: contentVStack.leadingAnchor),
reblogHStack.trailingAnchor.constraint(lessThanOrEqualTo: contentView.trailingAnchor, constant: -16), timelineReasonHStack.trailingAnchor.constraint(lessThanOrEqualTo: contentView.trailingAnchor, constant: -16),
mainContainer.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), mainContainer.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
mainContainer.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), mainContainer.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
@ -427,23 +431,35 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
self.statusState = state self.statusState = state
var hideTimelineReason = true
if let rebloggedStatus = status.reblog { if let rebloggedStatus = status.reblog {
reblogStatusID = statusID reblogStatusID = statusID
rebloggerID = status.account.id rebloggerID = status.account.id
reblogHStack.isHidden = false
mainContainerTopToReblogLabelConstraint.isActive = true
mainContainerTopToSelfConstraint.isActive = false
updateRebloggerLabel(reblogger: status.account)
hideTimelineReason = false
timelineReasonIcon.image = reblogIcon
updateRebloggerLabel(reblogger: status.account)
status = rebloggedStatus status = rebloggedStatus
} else { } else {
reblogStatusID = nil reblogStatusID = nil
rebloggerID = nil rebloggerID = nil
reblogHStack.isHidden = true
mainContainerTopToReblogLabelConstraint.isActive = false
mainContainerTopToSelfConstraint.isActive = true
} }
if showFollowedHashtags {
let hashtags = mastodonController.followedHashtags.filter({ followed in status.hashtags.contains(where: { followed.name == $0.name }) })
if !hashtags.isEmpty {
hideTimelineReason = false
timelineReasonIcon.image = hashtagIcon
timelineReasonLabel.text = hashtags.map(\.name).formatted(.list(type: .and, width: .narrow))
timelineReasonLabel.removeEmojis()
}
}
timelineReasonHStack.isHidden = hideTimelineReason
mainContainerTopToReblogLabelConstraint.isActive = !hideTimelineReason
mainContainerTopToSelfConstraint.isActive = hideTimelineReason
doUpdateUI(status: status) doUpdateUI(status: status)
doUpdateTimestamp(status: status) doUpdateTimestamp(status: status)
@ -523,11 +539,11 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
private func updateRebloggerLabel(reblogger: AccountMO) { private func updateRebloggerLabel(reblogger: AccountMO) {
if Preferences.shared.hideCustomEmojiInUsernames { if Preferences.shared.hideCustomEmojiInUsernames {
rebloggerLabel.text = "\(reblogger.displayNameWithoutCustomEmoji) reblogged" timelineReasonLabel.text = "\(reblogger.displayNameWithoutCustomEmoji) reblogged"
rebloggerLabel.removeEmojis() timelineReasonLabel.removeEmojis()
} else { } else {
rebloggerLabel.text = "\(reblogger.displayOrUserName) reblogged" timelineReasonLabel.text = "\(reblogger.displayOrUserName) reblogged"
rebloggerLabel.setEmojis(reblogger.emojis, identifier: reblogger.id) timelineReasonLabel.setEmojis(reblogger.emojis, identifier: reblogger.id)
} }
} }
@ -566,10 +582,13 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
// MARK: Interaction // MARK: Interaction
@objc private func reblogLabelPressed() { @objc private func reblogLabelPressed() {
guard let rebloggerID else { if let rebloggerID {
return delegate?.selected(account: rebloggerID)
} else if showFollowedHashtags,
let status = mastodonController.persistentContainer.status(for: statusID),
let hashtag = mastodonController.followedHashtags.first(where: { followed in status.hashtags.contains(where: { followed.name == $0.name }) }) {
delegate?.selected(tag: Hashtag(name: hashtag.name, url: hashtag.url))
} }
delegate?.selected(account: rebloggerID)
} }
@objc private func accountPressed() { @objc private func accountPressed() {