feat: update disappearing messages ui for visible messages

pull/731/head
Ryan Zhao 8 months ago
parent 61df9e2cf1
commit 3d1b930ede

@ -4,20 +4,15 @@ import UIKit
import SessionSnodeKit
class DisappearingMessageTimerView: UIView {
private var initialDurationSeconds: Int32 = 0
private var expirationTimestampMs: Int64 = 0
private var initialDurationSeconds: Double = 0
private var expirationTimestampMs: Double = 0
// MARK: - Animation
private var animationTimer: Timer?
private var progress: Int = 12 // 0 == about to expire, 12 == just started countdown.
// MARK: - UI
private var iconImageView: UIImageView = {
let result: UIImageView = UIImageView()
result.set(.width, to: 9)
result.set(.height, to: 9)
return result
}()
private var iconImageView = UIImageView()
// MARK: - Lifecycle
@ -25,7 +20,7 @@ class DisappearingMessageTimerView: UIView {
super.init(frame: CGRect.zero)
self.addSubview(iconImageView)
iconImageView.pin(to: self)
iconImageView.pin(to: self, withInset: 1)
}
override init(frame: CGRect) {
@ -36,7 +31,7 @@ class DisappearingMessageTimerView: UIView {
preconditionFailure("Use init(viewItem:textColor:) instead.")
}
public func configure(expirationTimestampMs: Int64, initialDurationSeconds: Int32) {
public func configure(expirationTimestampMs: Double, initialDurationSeconds: Double) {
self.expirationTimestampMs = expirationTimestampMs
self.initialDurationSeconds = initialDurationSeconds
@ -50,9 +45,9 @@ class DisappearingMessageTimerView: UIView {
return
}
let timestampMs: Int64 = SnodeAPI.currentOffsetTimestampMs()
let secondsLeft: Double = max(Double(self.expirationTimestampMs - timestampMs) / 1000, 0)
let progressRatio: Double = self.initialDurationSeconds > 0 ? secondsLeft / Double(self.initialDurationSeconds) : 0
let timestampMs: Double = Double(SnodeAPI.currentOffsetTimestampMs())
let secondsLeft: Double = max((self.expirationTimestampMs - timestampMs) / 1000, 0)
let progressRatio: Double = self.initialDurationSeconds > 0 ? secondsLeft / self.initialDurationSeconds : 0
self.progress = Int(round(min(progressRatio, 1) * 12))
self.updateIcon()

@ -36,8 +36,10 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
private lazy var underBubbleStackViewOutgoingTrailingConstraint: NSLayoutConstraint = underBubbleStackView.pin(.trailing, to: .trailing, of: snContentView)
private lazy var underBubbleStackViewNoHeightConstraint: NSLayoutConstraint = underBubbleStackView.set(.height, to: 0)
private lazy var timerViewOutgoingMessageConstraint = timerView.pin(.leading, to: .leading, of: self, withInset: VisibleMessageCell.contactThreadHSpacing)
private lazy var timerViewIncomingMessageConstraint = timerView.pin(.trailing, to: .trailing, of: self, withInset: -VisibleMessageCell.contactThreadHSpacing)
private lazy var timerViewOutgoingMessageConstraint = timerView.pin(.trailing, to: .trailing, of: messageStatusContainerView)
private lazy var timerViewIncomingMessageConstraint = timerView.pin(.leading, to: .leading, of: messageStatusContainerView)
private lazy var messageStatusLabelOutgoingMessageConstraint = messageStatusLabel.pin(.trailing, to: .leading, of: timerView, withInset: -2)
private lazy var messageStatusLabelIncomingMessageConstraint = messageStatusLabel.pin(.leading, to: .trailing, of: timerView, withInset: 2)
private lazy var panGestureRecognizer: UIPanGestureRecognizer = {
let result = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
@ -118,7 +120,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
return result
}()
private lazy var timerView: OWSMessageTimerView = OWSMessageTimerView()
private lazy var timerView = DisappearingMessageTimerView()
lazy var underBubbleStackView: UIStackView = {
let result = UIStackView(arrangedSubviews: [])
@ -215,11 +217,6 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
bubbleBackgroundView.addSubview(bubbleView)
bubbleView.pin(to: bubbleBackgroundView)
// Timer view
addSubview(timerView)
timerView.center(.vertical, in: snContentView)
timerViewOutgoingMessageConstraint.isActive = true
// Reply button
addSubview(replyButton)
replyButton.addSubview(replyIconImageView)
@ -242,6 +239,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
messageStatusContainerView.addSubview(messageStatusLabel)
messageStatusContainerView.addSubview(messageStatusImageView)
messageStatusContainerView.addSubview(timerView)
reactionContainerView.widthAnchor
.constraint(lessThanOrEqualTo: underBubbleStackView.widthAnchor)
@ -251,9 +249,12 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
messageStatusImageView.pin(.trailing, to: .trailing, of: messageStatusContainerView)
messageStatusImageView.set(.width, to: VisibleMessageCell.messageStatusImageViewSize)
messageStatusImageView.set(.height, to: VisibleMessageCell.messageStatusImageViewSize)
timerView.pin(.top, to: .top, of: messageStatusContainerView)
timerView.pin(.bottom, to: .bottom, of: messageStatusContainerView)
timerView.set(.width, to: VisibleMessageCell.messageStatusImageViewSize)
timerView.set(.height, to: VisibleMessageCell.messageStatusImageViewSize)
messageStatusLabel.center(.vertical, in: messageStatusContainerView)
messageStatusLabel.pin(.leading, to: .leading, of: messageStatusContainerView)
messageStatusLabel.pin(.trailing, to: .leading, of: messageStatusImageView, withInset: -2)
// messageStatusLabel.pin(.leading, to: .leading, of: messageStatusContainerView)
messageStatusLabelPaddingView.pin(.leading, to: .leading, of: messageStatusContainerView)
messageStatusLabelPaddingView.pin(.trailing, to: .trailing, of: messageStatusContainerView)
}
@ -359,31 +360,7 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
let authorLabelAvailableSpace = CGSize(width: authorLabelAvailableWidth, height: .greatestFiniteMagnitude)
let authorLabelSize = authorLabel.sizeThatFits(authorLabelAvailableSpace)
authorLabelHeightConstraint.constant = (cellViewModel.senderName != nil ? authorLabelSize.height : 0)
// Timer
if
let expiresStartedAtMs: Double = cellViewModel.expiresStartedAtMs,
let expiresInSeconds: TimeInterval = cellViewModel.expiresInSeconds
{
let expirationTimestampMs: Double = (expiresStartedAtMs + (expiresInSeconds * 1000))
timerView.configure(
withExpirationTimestamp: UInt64(floor(expirationTimestampMs)),
initialDurationSeconds: UInt32(floor(expiresInSeconds))
)
timerView.themeTintColor = .textPrimary
timerView.isHidden = false
}
else {
timerView.isHidden = true
}
timerViewOutgoingMessageConstraint.isActive = (cellViewModel.variant == .standardOutgoing)
timerViewIncomingMessageConstraint.isActive = (
cellViewModel.variant == .standardIncoming ||
cellViewModel.variant == .standardIncomingDeleted
)
// Swipe to reply
if ContextMenuVC.viewModelCanReply(cellViewModel) {
addGestureRecognizer(panGestureRecognizer)
@ -424,11 +401,13 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
messageStatusLabel.accessibilityIdentifier = "Message sent status: \(statusText ?? "invalid")"
messageStatusImageView.themeTintColor = tintColor
messageStatusContainerView.isHidden = (
cellViewModel.variant != .standardOutgoing ||
cellViewModel.variant == .infoCall ||
(
cellViewModel.state == .sent &&
!cellViewModel.isLastOutgoing
(cellViewModel.expiresInSeconds ?? 0) == 0 && (
cellViewModel.variant != .standardOutgoing ||
cellViewModel.variant == .infoCall ||
(
cellViewModel.state == .sent &&
!cellViewModel.isLastOutgoing
)
)
)
messageStatusLabelPaddingView.isHidden = (
@ -436,6 +415,37 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
cellViewModel.isLast
)
// Timer
if
let expiresStartedAtMs: Double = cellViewModel.expiresStartedAtMs,
let expiresInSeconds: TimeInterval = cellViewModel.expiresInSeconds
{
let expirationTimestampMs: Double = (expiresStartedAtMs + (expiresInSeconds * 1000))
timerView.configure(
expirationTimestampMs: expirationTimestampMs,
initialDurationSeconds: expiresInSeconds
)
timerView.themeTintColor = tintColor
timerView.isHidden = false
messageStatusImageView.isHidden = true
}
else {
timerView.isHidden = true
messageStatusImageView.isHidden = false
}
timerViewOutgoingMessageConstraint.isActive = (cellViewModel.variant == .standardOutgoing)
timerViewIncomingMessageConstraint.isActive = (
cellViewModel.variant == .standardIncoming ||
cellViewModel.variant == .standardIncomingDeleted
)
messageStatusLabelOutgoingMessageConstraint.isActive = (cellViewModel.variant == .standardOutgoing)
messageStatusLabelIncomingMessageConstraint.isActive = (
cellViewModel.variant == .standardIncoming ||
cellViewModel.variant == .standardIncomingDeleted
)
// Set the height of the underBubbleStackView to 0 if it has no content (need to do this
// otherwise it can randomly stretch)
underBubbleStackViewNoHeightConstraint.isActive = underBubbleStackView.arrangedSubviews

@ -71,7 +71,7 @@ public struct RecipientState: Codable, Equatable, FetchableRecord, PersistableRe
}
public func statusIconInfo(variant: Interaction.Variant, hasAtLeastOneReadReceipt: Bool) -> (image: UIImage?, text: String?, themeTintColor: ThemeValue) {
guard variant == .standardOutgoing else { return (nil, nil, .textPrimary) }
guard variant == .standardOutgoing else { return (nil, "MESSAGE_DELIVERY_STATUS_READ".localized(), .messageBubble_deliveryStatus) }
switch (self, hasAtLeastOneReadReceipt) {
case (.sending, _):

Loading…
Cancel
Save