feat: display toot poll status
This commit is contained in:
parent
aea2ddc078
commit
376cb3d58a
|
@ -58,7 +58,14 @@
|
|||
"user_boosted": "%s boosted",
|
||||
"show_post": "Show Post",
|
||||
"status_content_warning": "content warning",
|
||||
"media_content_warning": "Tap to reveal that may be sensitive"
|
||||
"media_content_warning": "Tap to reveal that may be sensitive",
|
||||
"poll": {
|
||||
"vote_count": {
|
||||
"single": "%d vote",
|
||||
"multiple": "%d votes",
|
||||
},
|
||||
"time_left": "%s left"
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"load_more": "Load More"
|
||||
|
|
|
@ -132,14 +132,14 @@ extension StatusSection {
|
|||
}()
|
||||
if mosiacImageViewModel.metas.count == 1 {
|
||||
let meta = mosiacImageViewModel.metas[0]
|
||||
let imageView = cell.statusView.statusMosaicImageView.setupImageView(aspectRatio: meta.size, maxSize: imageViewMaxSize)
|
||||
let imageView = cell.statusView.statusMosaicImageViewContainer.setupImageView(aspectRatio: meta.size, maxSize: imageViewMaxSize)
|
||||
imageView.af.setImage(
|
||||
withURL: meta.url,
|
||||
placeholderImage: UIImage.placeholder(color: .systemFill),
|
||||
imageTransition: .crossDissolve(0.2)
|
||||
)
|
||||
} else {
|
||||
let imageViews = cell.statusView.statusMosaicImageView.setupImageViews(count: mosiacImageViewModel.metas.count, maxHeight: imageViewMaxSize.height)
|
||||
let imageViews = cell.statusView.statusMosaicImageViewContainer.setupImageViews(count: mosiacImageViewModel.metas.count, maxHeight: imageViewMaxSize.height)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
let meta = mosiacImageViewModel.metas[i]
|
||||
imageView.af.setImage(
|
||||
|
@ -149,18 +149,19 @@ extension StatusSection {
|
|||
)
|
||||
}
|
||||
}
|
||||
cell.statusView.statusMosaicImageView.isHidden = mosiacImageViewModel.metas.isEmpty
|
||||
cell.statusView.statusMosaicImageViewContainer.isHidden = mosiacImageViewModel.metas.isEmpty
|
||||
let isStatusSensitive = statusContentWarningAttribute?.isStatusSensitive ?? (toot.reblog ?? toot).sensitive
|
||||
cell.statusView.statusMosaicImageView.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil
|
||||
cell.statusView.statusMosaicImageView.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0
|
||||
cell.statusView.statusMosaicImageViewContainer.blurVisualEffectView.effect = isStatusSensitive ? MosaicImageViewContainer.blurVisualEffect : nil
|
||||
cell.statusView.statusMosaicImageViewContainer.vibrancyVisualEffectView.alpha = isStatusSensitive ? 1.0 : 0.0
|
||||
|
||||
// set poll
|
||||
if let poll = (toot.reblog ?? toot).poll {
|
||||
cell.statusView.statusPollTableView.isHidden = false
|
||||
cell.statusView.pollTableView.isHidden = false
|
||||
cell.statusView.pollStatusStackView.isHidden = false
|
||||
|
||||
let managedObjectContext = toot.managedObjectContext!
|
||||
cell.statusView.statusPollTableViewDataSource = PollSection.tableViewDiffableDataSource(
|
||||
for: cell.statusView.statusPollTableView,
|
||||
for: cell.statusView.pollTableView,
|
||||
managedObjectContext: managedObjectContext
|
||||
)
|
||||
|
||||
|
@ -176,9 +177,9 @@ extension StatusSection {
|
|||
}
|
||||
snapshot.appendItems(pollItems, toSection: .main)
|
||||
cell.statusView.statusPollTableViewDataSource?.apply(snapshot, animatingDifferences: false, completion: nil)
|
||||
// cell.statusView.statusPollTableView.layoutIfNeeded()
|
||||
} else {
|
||||
cell.statusView.statusPollTableView.isHidden = true
|
||||
cell.statusView.pollTableView.isHidden = true
|
||||
cell.statusView.pollStatusStackView.isHidden = true
|
||||
}
|
||||
|
||||
// toolbar
|
||||
|
|
|
@ -68,6 +68,22 @@ internal enum L10n {
|
|||
internal static func userBoosted(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Status.UserBoosted", String(describing: p1))
|
||||
}
|
||||
internal enum Poll {
|
||||
/// %@ left
|
||||
internal static func timeLeft(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Status.Poll.TimeLeft", String(describing: p1))
|
||||
}
|
||||
internal enum VoteCount {
|
||||
/// %d votes
|
||||
internal static func multiple(_ p1: Int) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Status.Poll.VoteCount.Multiple", p1)
|
||||
}
|
||||
/// %d vote
|
||||
internal static func single(_ p1: Int) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Status.Poll.VoteCount.Single", p1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
internal enum Timeline {
|
||||
/// Load More
|
||||
|
@ -124,7 +140,7 @@ internal enum L10n {
|
|||
internal static let passwordTooShrot = L10n.tr("Localizable", "Common.Errors.Itemdetail.PasswordTooShrot")
|
||||
/// Username must only contain alphanumeric characters and underscores
|
||||
internal static let usernameInvalid = L10n.tr("Localizable", "Common.Errors.Itemdetail.UsernameInvalid")
|
||||
/// username is too long ( can't be longer than 30 characters)
|
||||
/// username is too long (can't be longer than 30 characters)
|
||||
internal static let usernameTooLong = L10n.tr("Localizable", "Common.Errors.Itemdetail.UsernameTooLong")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,8 +69,8 @@ extension StatusTableViewCellDelegate where Self: StatusProvider {
|
|||
var snapshot = diffableDataSource.snapshot()
|
||||
snapshot.reloadItems([item])
|
||||
UIView.animate(withDuration: 0.33) {
|
||||
cell.statusView.statusMosaicImageView.blurVisualEffectView.effect = nil
|
||||
cell.statusView.statusMosaicImageView.vibrancyVisualEffectView.alpha = 0.0
|
||||
cell.statusView.statusMosaicImageViewContainer.blurVisualEffectView.effect = nil
|
||||
cell.statusView.statusMosaicImageViewContainer.vibrancyVisualEffectView.alpha = 0.0
|
||||
} completion: { _ in
|
||||
diffableDataSource.apply(snapshot, animatingDifferences: false, completion: nil)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
"Common.Controls.Actions.SignUp" = "Sign Up";
|
||||
"Common.Controls.Actions.TakePhoto" = "Take photo";
|
||||
"Common.Controls.Status.MediaContentWarning" = "Tap to reveal that may be sensitive";
|
||||
"Common.Controls.Status.Poll.TimeLeft" = "%@ left";
|
||||
"Common.Controls.Status.Poll.VoteCount.Multiple" = "%d votes";
|
||||
"Common.Controls.Status.Poll.VoteCount.Single" = "%d vote";
|
||||
"Common.Controls.Status.ShowPost" = "Show Post";
|
||||
"Common.Controls.Status.StatusContentWarning" = "content warning";
|
||||
"Common.Controls.Status.UserBoosted" = "%@ boosted";
|
||||
|
@ -42,7 +45,7 @@
|
|||
"Common.Errors.Itemdetail.EmailInvalid" = "This is not a valid e-mail address";
|
||||
"Common.Errors.Itemdetail.PasswordTooShrot" = "password is too short (must be at least 8 characters)";
|
||||
"Common.Errors.Itemdetail.UsernameInvalid" = "Username must only contain alphanumeric characters and underscores";
|
||||
"Common.Errors.Itemdetail.UsernameTooLong" = "username is too long ( can't be longer than 30 characters)";
|
||||
"Common.Errors.Itemdetail.UsernameTooLong" = "username is too long (can't be longer than 30 characters)";
|
||||
"Scene.ConfirmEmail.Button.DontReceiveEmail" = "I never got an email";
|
||||
"Scene.ConfirmEmail.Button.OpenEmailApp" = "Open Email App";
|
||||
"Scene.ConfirmEmail.DontReceiveEmail.Description" = "Check if your email address is correct as well as your junk folder if you haven’t.";
|
||||
|
|
|
@ -103,9 +103,9 @@ final class StatusView: UIView {
|
|||
button.setTitle(L10n.Common.Controls.Status.showPost, for: .normal)
|
||||
return button
|
||||
}()
|
||||
let statusMosaicImageView = MosaicImageViewContainer()
|
||||
let statusMosaicImageViewContainer = MosaicImageViewContainer()
|
||||
|
||||
let statusPollTableView: UITableView = {
|
||||
let pollTableView: UITableView = {
|
||||
let tableView = UITableView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
|
||||
tableView.register(PollOptionTableViewCell.self, forCellReuseIdentifier: String(describing: PollOptionTableViewCell.self))
|
||||
tableView.isScrollEnabled = false
|
||||
|
@ -114,6 +114,29 @@ final class StatusView: UIView {
|
|||
return tableView
|
||||
}()
|
||||
|
||||
let pollStatusStackView = UIStackView()
|
||||
let pollVoteCountLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 12, weight: .regular))
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.text = L10n.Common.Controls.Status.Poll.VoteCount.single(0)
|
||||
return label
|
||||
}()
|
||||
let pollStatusDotLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 12, weight: .regular))
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.text = " · "
|
||||
return label
|
||||
}()
|
||||
let pollCountdownLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 12, weight: .regular))
|
||||
label.textColor = Asset.Colors.Label.secondary.color
|
||||
label.text = L10n.Common.Controls.Status.Poll.timeLeft("6 hours")
|
||||
return label
|
||||
}()
|
||||
|
||||
// do not use visual effect view due to we blur text only without background
|
||||
let contentWarningBlurContentImageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
|
@ -239,7 +262,7 @@ extension StatusView {
|
|||
subtitleContainerStackView.axis = .horizontal
|
||||
subtitleContainerStackView.addArrangedSubview(usernameLabel)
|
||||
|
||||
// status container: [status | image / video | audio | poll]
|
||||
// status container: [status | image / video | audio | poll | poll status]
|
||||
containerStackView.addArrangedSubview(statusContainerStackView)
|
||||
statusContainerStackView.axis = .vertical
|
||||
statusContainerStackView.spacing = 10
|
||||
|
@ -275,30 +298,37 @@ extension StatusView {
|
|||
statusContentWarningContainerStackView.addArrangedSubview(contentWarningTitle)
|
||||
statusContentWarningContainerStackView.addArrangedSubview(contentWarningActionButton)
|
||||
|
||||
statusContainerStackView.addArrangedSubview(statusMosaicImageView)
|
||||
statusPollTableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusContainerStackView.addArrangedSubview(statusPollTableView)
|
||||
statusPollTableViewHeightLaoutConstraint = statusPollTableView.heightAnchor.constraint(equalToConstant: 44.0).priority(.required - 1)
|
||||
statusContainerStackView.addArrangedSubview(statusMosaicImageViewContainer)
|
||||
pollTableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
statusContainerStackView.addArrangedSubview(pollTableView)
|
||||
statusPollTableViewHeightLaoutConstraint = pollTableView.heightAnchor.constraint(equalToConstant: 44.0).priority(.required - 1)
|
||||
NSLayoutConstraint.activate([
|
||||
statusPollTableViewHeightLaoutConstraint,
|
||||
])
|
||||
|
||||
statusPollTableViewHeightObservation = statusPollTableView.observe(\.contentSize, options: .new, changeHandler: { [weak self] tableView, _ in
|
||||
statusPollTableViewHeightObservation = pollTableView.observe(\.contentSize, options: .new, changeHandler: { [weak self] tableView, _ in
|
||||
guard let self = self else { return }
|
||||
guard self.statusPollTableView.contentSize.height != .zero else {
|
||||
guard self.pollTableView.contentSize.height != .zero else {
|
||||
self.statusPollTableViewHeightLaoutConstraint.constant = 44
|
||||
return
|
||||
}
|
||||
self.statusPollTableViewHeightLaoutConstraint.constant = self.statusPollTableView.contentSize.height
|
||||
self.statusPollTableViewHeightLaoutConstraint.constant = self.pollTableView.contentSize.height
|
||||
})
|
||||
|
||||
statusContainerStackView.addArrangedSubview(pollStatusStackView)
|
||||
pollStatusStackView.axis = .horizontal
|
||||
pollStatusStackView.addArrangedSubview(pollVoteCountLabel)
|
||||
pollStatusStackView.addArrangedSubview(pollStatusDotLabel)
|
||||
pollStatusStackView.addArrangedSubview(pollCountdownLabel)
|
||||
|
||||
// action toolbar container
|
||||
containerStackView.addArrangedSubview(actionToolbarContainer)
|
||||
actionToolbarContainer.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
||||
|
||||
headerContainerStackView.isHidden = true
|
||||
statusMosaicImageView.isHidden = true
|
||||
statusPollTableView.isHidden = true
|
||||
statusMosaicImageViewContainer.isHidden = true
|
||||
pollTableView.isHidden = true
|
||||
pollStatusStackView.isHidden = true
|
||||
|
||||
contentWarningBlurContentImageView.isHidden = true
|
||||
statusContentWarningContainerStackView.isHidden = true
|
||||
|
@ -390,11 +420,11 @@ struct StatusView_Previews: PreviewProvider {
|
|||
statusView.drawContentWarningImageView()
|
||||
statusView.updateContentWarningDisplay(isHidden: false)
|
||||
let images = MosaicImageView_Previews.images
|
||||
let imageViews = statusView.statusMosaicImageView.setupImageViews(count: 4, maxHeight: 162)
|
||||
let imageViews = statusView.statusMosaicImageViewContainer.setupImageViews(count: 4, maxHeight: 162)
|
||||
for (i, imageView) in imageViews.enumerated() {
|
||||
imageView.image = images[i]
|
||||
}
|
||||
statusView.statusMosaicImageView.isHidden = false
|
||||
statusView.statusMosaicImageViewContainer.isHidden = false
|
||||
return statusView
|
||||
}
|
||||
.previewLayout(.fixed(width: 375, height: 380))
|
||||
|
|
|
@ -32,7 +32,7 @@ final class StatusTableViewCell: UITableViewCell {
|
|||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
statusView.isStatusTextSensitive = false
|
||||
statusView.statusPollTableView.dataSource = nil
|
||||
statusView.pollTableView.dataSource = nil
|
||||
statusView.cleanUpContentWarning()
|
||||
disposeBag.removeAll()
|
||||
observations.removeAll()
|
||||
|
@ -85,7 +85,7 @@ extension StatusTableViewCell {
|
|||
bottomPaddingView.backgroundColor = Asset.Colors.Background.systemGroupedBackground.color
|
||||
|
||||
statusView.delegate = self
|
||||
statusView.statusMosaicImageView.delegate = self
|
||||
statusView.statusMosaicImageViewContainer.delegate = self
|
||||
statusView.actionToolbarContainer.delegate = self
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue