Indicate when a followed hashtag caused a post to appear in the home timeline
This commit is contained in:
parent
97d5b955a0
commit
c6da754875
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func accountPressed() {
|
@objc private func accountPressed() {
|
||||||
|
|
Loading…
Reference in New Issue