// // ExpandThreadCollectionViewCell.swift // Tusker // // Created by Shadowfacts on 1/30/21. // Copyright © 2021 Shadowfacts. All rights reserved. // import UIKit class ExpandThreadCollectionViewCell: UICollectionViewListCell { private var avatarContainerView: UIView! private var avatarContainerWidthConstraint: NSLayoutConstraint! private var replyCountLabel: UILabel! private var hStack: UIStackView! private var stackViewLeadingConstraint: NSLayoutConstraint! private var threadLinkView: UIView! private var threadLinkViewFullHeightConstraint: NSLayoutConstraint! private var threadLinkViewShortHeightConstraint: NSLayoutConstraint! private var avatarImageViews: [UIImageView] = [] override init(frame: CGRect) { super.init(frame: frame) avatarContainerView = UIView() avatarContainerView.backgroundColor = .clear avatarContainerWidthConstraint = avatarContainerView.widthAnchor.constraint(equalToConstant: 100) replyCountLabel = UILabel() replyCountLabel.textColor = .tintColor replyCountLabel.font = .preferredFont(forTextStyle: .body) replyCountLabel.adjustsFontForContentSizeCategory = true hStack = UIStackView(arrangedSubviews: [ avatarContainerView, replyCountLabel, ]) hStack.spacing = 8 hStack.alignment = .center hStack.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(hStack) stackViewLeadingConstraint = hStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16) threadLinkView = UIView() threadLinkView.backgroundColor = .tintColor.withAlphaComponent(0.5) threadLinkView.layer.cornerRadius = 2.5 threadLinkView.translatesAutoresizingMaskIntoConstraints = false contentView.addSubview(threadLinkView) threadLinkViewFullHeightConstraint = threadLinkView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) threadLinkViewShortHeightConstraint = threadLinkView.bottomAnchor.constraint(equalTo: avatarContainerView.topAnchor, constant: -2) NSLayoutConstraint.activate([ avatarContainerWidthConstraint, avatarContainerView.heightAnchor.constraint(equalToConstant: 32), stackViewLeadingConstraint, hStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), hStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), hStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8), threadLinkView.widthAnchor.constraint(equalToConstant: 5), threadLinkView.topAnchor.constraint(equalTo: contentView.topAnchor), threadLinkView.centerXAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16 + (50 / 2)) ]) NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func updateUI(childThreads: [ConversationNode], inline: Bool) { stackViewLeadingConstraint.constant = 16 + (inline ? 50 + 4 : 0) threadLinkView.layer.maskedCorners = inline ? [] : [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] threadLinkViewFullHeightConstraint.isActive = inline threadLinkViewShortHeightConstraint.isActive = !inline let format: String if inline { format = NSLocalizedString("expand threads inline count", comment: "expand conversation threads inline button label") } else { format = NSLocalizedString("expand threads count", comment: "expand conversation threads button label") } replyCountLabel.text = String.localizedStringWithFormat(format, childThreads.count) let accounts = childThreads.map(\.status.account).uniques().prefix(3) avatarImageViews.forEach { $0.removeFromSuperview() } avatarImageViews = [] let avatarImageSize: CGFloat = 44 - 12 if accounts.count == 1 { avatarContainerWidthConstraint.constant = avatarImageSize } else { avatarContainerWidthConstraint.constant = CGFloat(accounts.count) * avatarImageSize * 3 / 4 } for (index, account) in accounts.enumerated() { let accountImageView = CachedImageView(cache: .avatars) accountImageView.translatesAutoresizingMaskIntoConstraints = false accountImageView.contentMode = .scaleAspectFit accountImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * avatarImageSize accountImageView.layer.masksToBounds = true accountImageView.layer.borderWidth = 1 accountImageView.layer.borderColor = UIColor.secondarySystemBackground.cgColor // need a solid background color so semi-transparent avatars don't look bad accountImageView.backgroundColor = .secondarySystemBackground avatarContainerView.addSubview(accountImageView) avatarImageViews.append(accountImageView) accountImageView.layer.zPosition = CGFloat(-index) let xConstraint: NSLayoutConstraint if index == 0 { xConstraint = accountImageView.leadingAnchor.constraint(equalTo: avatarContainerView.leadingAnchor) } else if index == accounts.count - 1 { xConstraint = accountImageView.trailingAnchor.constraint(equalTo: avatarContainerView.trailingAnchor) } else { xConstraint = accountImageView.centerXAnchor.constraint(equalTo: avatarContainerView.centerXAnchor) } NSLayoutConstraint.activate([ accountImageView.widthAnchor.constraint(equalToConstant: avatarImageSize), accountImageView.heightAnchor.constraint(equalToConstant: avatarImageSize), accountImageView.centerYAnchor.constraint(equalTo: avatarContainerView.centerYAnchor), xConstraint, ]) accountImageView.update(for: account.avatar) } } @objc private func preferencesChanged() { for view in avatarImageViews { view.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: view) } } override func updateConfiguration(using state: UICellConfigurationState) { var config = UIBackgroundConfiguration.listPlainCell() if state.isSelected || state.isHighlighted { var hue: CGFloat = 0 var saturation: CGFloat = 0 var brightness: CGFloat = 0 UIColor.secondarySystemBackground.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) let sign: CGFloat = traitCollection.userInterfaceStyle == .dark ? 1 : -1 config.backgroundColor = UIColor(hue: hue, saturation: saturation, brightness: max(0, brightness + sign * 0.1), alpha: 1) } else { config.backgroundColor = .secondarySystemBackground } backgroundConfiguration = config.updated(for: state) } }