Форвард: Telegram-parity UI — правильный размер бабла, текст/таймстамп, аватарка с инициалами, отступы
This commit is contained in:
@@ -242,6 +242,7 @@ struct ChatDetailView: View {
|
||||
// setDialogActive only touches MessageRepository.activeDialogs (Set),
|
||||
// does NOT mutate DialogRepository, so ForEach won't rebuild.
|
||||
MessageRepository.shared.setDialogActive(route.publicKey, isActive: true)
|
||||
SessionManager.shared.resetIdleTimer()
|
||||
updateReadEligibility()
|
||||
clearDeliveredNotifications(for: route.publicKey)
|
||||
// Telegram-like read policy: mark read only when dialog is truly readable
|
||||
@@ -275,6 +276,7 @@ struct ChatDetailView: View {
|
||||
isViewActive = false
|
||||
updateReadEligibility()
|
||||
MessageRepository.shared.setDialogActive(route.publicKey, isActive: false)
|
||||
SessionManager.shared.stopIdleTimer()
|
||||
// Desktop parity: save draft text on chat close.
|
||||
DraftManager.shared.saveDraft(for: route.publicKey, text: messageText)
|
||||
}
|
||||
@@ -285,6 +287,7 @@ struct ChatDetailView: View {
|
||||
// 600ms delay lets notification-tap navigation settle — if user tapped
|
||||
// a notification for a DIFFERENT chat, isViewActive becomes false.
|
||||
guard isViewActive else { return }
|
||||
SessionManager.shared.resetIdleTimer()
|
||||
Task { @MainActor in
|
||||
try? await Task.sleep(for: .milliseconds(600))
|
||||
guard isViewActive else { return }
|
||||
@@ -838,6 +841,7 @@ private extension ChatDetailView {
|
||||
scrollToBottomRequested: $scrollToBottomRequested,
|
||||
onAtBottomChange: { atBottom in
|
||||
isAtBottom = atBottom
|
||||
SessionManager.shared.resetIdleTimer()
|
||||
updateReadEligibility()
|
||||
if atBottom {
|
||||
markDialogAsRead()
|
||||
|
||||
@@ -172,25 +172,25 @@ struct MessageCellView: View, Equatable {
|
||||
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("Forwarded from")
|
||||
.font(.system(size: 13, weight: .regular))
|
||||
.foregroundStyle(outgoing ? Color.white.opacity(0.5) : RosettaColors.Adaptive.textSecondary)
|
||||
.font(.system(size: 14, weight: .regular))
|
||||
.foregroundStyle(outgoing ? Color.white : RosettaColors.figmaBlue)
|
||||
.padding(.leading, 11)
|
||||
.padding(.top, 6)
|
||||
.padding(.top, 7)
|
||||
|
||||
HStack(spacing: 6) {
|
||||
HStack(spacing: 4) {
|
||||
AvatarView(
|
||||
initials: senderInitials,
|
||||
colorIndex: senderColorIndex,
|
||||
size: 20,
|
||||
size: 16,
|
||||
image: senderAvatar
|
||||
)
|
||||
Text(senderName)
|
||||
.font(.system(size: 14, weight: .semibold))
|
||||
.font(.system(size: 14, weight: .medium))
|
||||
.foregroundStyle(outgoing ? Color.white : RosettaColors.figmaBlue)
|
||||
.lineLimit(1)
|
||||
}
|
||||
.padding(.leading, 11)
|
||||
.padding(.top, 3)
|
||||
.padding(.top, 0)
|
||||
|
||||
if !imageAttachments.isEmpty {
|
||||
ForwardedPhotoCollageView(
|
||||
|
||||
@@ -19,8 +19,8 @@ final class NativeMessageCell: UICollectionViewCell {
|
||||
private static let timestampFont = UIFont.systemFont(ofSize: floor(textFont.pointSize * 11.0 / 17.0), weight: .regular)
|
||||
private static let replyNameFont = UIFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
private static let replyTextFont = UIFont.systemFont(ofSize: 14, weight: .regular)
|
||||
private static let forwardLabelFont = UIFont.systemFont(ofSize: 13, weight: .regular)
|
||||
private static let forwardNameFont = UIFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
private static let forwardLabelFont = UIFont.systemFont(ofSize: 14, weight: .regular)
|
||||
private static let forwardNameFont = UIFont.systemFont(ofSize: 14, weight: .medium)
|
||||
private static let fileNameFont = UIFont.systemFont(ofSize: 16, weight: .regular)
|
||||
private static let fileSizeFont = UIFont.monospacedDigitSystemFont(ofSize: 13, weight: .regular)
|
||||
private static let bubbleMetrics = BubbleMetrics.telegram()
|
||||
@@ -136,6 +136,8 @@ final class NativeMessageCell: UICollectionViewCell {
|
||||
// Forward header
|
||||
private let forwardLabel = UILabel()
|
||||
private let forwardAvatarView = UIView()
|
||||
private let forwardAvatarInitialLabel = UILabel()
|
||||
private let forwardAvatarImageView = UIImageView()
|
||||
private let forwardNameLabel = UILabel()
|
||||
|
||||
// Highlight overlay (scroll-to-message flash)
|
||||
@@ -397,16 +399,24 @@ final class NativeMessageCell: UICollectionViewCell {
|
||||
|
||||
// Forward header
|
||||
forwardLabel.font = Self.forwardLabelFont
|
||||
forwardLabel.text = "Forwarded message"
|
||||
forwardLabel.textColor = UIColor.white.withAlphaComponent(0.6)
|
||||
forwardLabel.text = "Forwarded from"
|
||||
bubbleView.addSubview(forwardLabel)
|
||||
|
||||
forwardAvatarView.backgroundColor = UIColor.white.withAlphaComponent(0.3)
|
||||
forwardAvatarView.layer.cornerRadius = 10
|
||||
forwardAvatarView.layer.cornerRadius = 8
|
||||
forwardAvatarView.clipsToBounds = true
|
||||
bubbleView.addSubview(forwardAvatarView)
|
||||
|
||||
forwardAvatarInitialLabel.font = .systemFont(ofSize: 8, weight: .medium)
|
||||
forwardAvatarInitialLabel.textColor = .white
|
||||
forwardAvatarInitialLabel.textAlignment = .center
|
||||
forwardAvatarView.addSubview(forwardAvatarInitialLabel)
|
||||
|
||||
forwardAvatarImageView.contentMode = .scaleAspectFill
|
||||
forwardAvatarImageView.clipsToBounds = true
|
||||
forwardAvatarView.addSubview(forwardAvatarImageView)
|
||||
|
||||
forwardNameLabel.font = Self.forwardNameFont
|
||||
forwardNameLabel.textColor = .white
|
||||
forwardNameLabel.textColor = .white // default, overridden in configure()
|
||||
bubbleView.addSubview(forwardNameLabel)
|
||||
|
||||
// Highlight overlay — on top of all bubble content
|
||||
@@ -457,7 +467,8 @@ final class NativeMessageCell: UICollectionViewCell {
|
||||
replyName: String? = nil,
|
||||
replyText: String? = nil,
|
||||
replyMessageId: String? = nil,
|
||||
forwardSenderName: String? = nil
|
||||
forwardSenderName: String? = nil,
|
||||
forwardSenderKey: String? = nil
|
||||
) {
|
||||
self.message = message
|
||||
self.actions = actions
|
||||
@@ -542,6 +553,31 @@ final class NativeMessageCell: UICollectionViewCell {
|
||||
forwardAvatarView.isHidden = false
|
||||
forwardNameLabel.isHidden = false
|
||||
forwardNameLabel.text = forwardSenderName
|
||||
// Telegram: same accentTextColor for both title and name
|
||||
let accent: UIColor = isOutgoing ? .white : Self.outgoingColor
|
||||
forwardLabel.textColor = accent
|
||||
forwardNameLabel.textColor = accent
|
||||
// Avatar: real photo if available, otherwise initial + color
|
||||
if let key = forwardSenderKey, let avatarImage = AvatarRepository.shared.loadAvatar(publicKey: key) {
|
||||
forwardAvatarImageView.image = avatarImage
|
||||
forwardAvatarImageView.isHidden = false
|
||||
forwardAvatarInitialLabel.isHidden = true
|
||||
forwardAvatarView.backgroundColor = .clear
|
||||
} else {
|
||||
forwardAvatarImageView.image = nil
|
||||
forwardAvatarImageView.isHidden = true
|
||||
forwardAvatarInitialLabel.isHidden = false
|
||||
let initial = String(forwardSenderName.prefix(1)).uppercased()
|
||||
forwardAvatarInitialLabel.text = initial
|
||||
let colorIndex = RosettaColors.avatarColorIndex(for: forwardSenderName, publicKey: forwardSenderKey ?? "")
|
||||
let hexes: [UInt32] = [0x228be6, 0x15aabf, 0xbe4bdb, 0x40c057, 0x4c6ef5, 0x82c91e, 0xfd7e14, 0xe64980, 0xfa5252, 0x12b886, 0x7950f2]
|
||||
let hex = hexes[colorIndex % hexes.count]
|
||||
forwardAvatarView.backgroundColor = UIColor(
|
||||
red: CGFloat((hex >> 16) & 0xFF) / 255,
|
||||
green: CGFloat((hex >> 8) & 0xFF) / 255,
|
||||
blue: CGFloat(hex & 0xFF) / 255, alpha: 1
|
||||
)
|
||||
}
|
||||
} else {
|
||||
forwardLabel.isHidden = true
|
||||
forwardAvatarView.isHidden = true
|
||||
@@ -894,6 +930,9 @@ final class NativeMessageCell: UICollectionViewCell {
|
||||
if layout.isForward {
|
||||
forwardLabel.frame = layout.forwardHeaderFrame
|
||||
forwardAvatarView.frame = layout.forwardAvatarFrame
|
||||
let avatarBounds = forwardAvatarView.bounds
|
||||
forwardAvatarInitialLabel.frame = avatarBounds
|
||||
forwardAvatarImageView.frame = avatarBounds
|
||||
forwardNameLabel.frame = layout.forwardNameFrame
|
||||
}
|
||||
|
||||
|
||||
@@ -271,6 +271,7 @@ final class NativeMessageListController: UIViewController {
|
||||
var replyText: String?
|
||||
var replyMessageId: String?
|
||||
var forwardSenderName: String?
|
||||
var forwardSenderKey: String?
|
||||
|
||||
if let att = replyAtt {
|
||||
if let data = att.blob.data(using: .utf8),
|
||||
@@ -293,6 +294,7 @@ final class NativeMessageListController: UIViewController {
|
||||
if displayText.isEmpty {
|
||||
// Forward
|
||||
forwardSenderName = name
|
||||
forwardSenderKey = senderKey
|
||||
} else {
|
||||
// Reply quote
|
||||
replyName = name
|
||||
@@ -312,7 +314,8 @@ final class NativeMessageListController: UIViewController {
|
||||
replyName: replyName,
|
||||
replyText: replyText,
|
||||
replyMessageId: replyMessageId,
|
||||
forwardSenderName: forwardSenderName
|
||||
forwardSenderName: forwardSenderName,
|
||||
forwardSenderKey: forwardSenderKey
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user