feat: display toot poll status

This commit is contained in:
CMK 2021-03-02 19:33:33 +08:00
parent aea2ddc078
commit 376cb3d58a
7 changed files with 87 additions and 30 deletions

View File

@ -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"

View File

@ -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

View File

@ -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")
}
}

View File

@ -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)
}

View File

@ -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 havent.";

View File

@ -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))

View File

@ -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
}