Display a badge for messages decrypted using shared keys. (#4937)

* feat: Show info badge for messages decrypted with shared keys

* tests: Add snapshots and tests for forwarder icon.

* fix: Update `EventBasedTimelineItemProtocol.hasStatusIcon`

* fix: Address review comments.

* fix: Use two translations for key forwarded dialog content.

* refactor: Rename `TimelineItemForwarder` to `TimelineItemKeyForwarder`.

Additionally adds a `[weak self]` guard to `encryptionForwarder` alert
handler.

* tests: Add `previewLayout` directive to `encryptionForwarder` previews

* refactor: Remove unused translations, use short-circuiting condition.
This commit is contained in:
Skye Elliot
2026-01-19 14:27:49 +00:00
committed by GitHub
parent 20e15f0e3e
commit a30ae24d16
19 changed files with 282 additions and 24 deletions
@@ -44,7 +44,7 @@ extension EventBasedTimelineItemProtocol {
}
var hasStatusIcon: Bool {
hasFailedToSend || properties.encryptionAuthenticity != nil
hasFailedToSend || properties.encryptionAuthenticity != nil || properties.encryptionForwarder != nil
}
var hasFailedToSend: Bool {
@@ -126,7 +126,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildImageTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -149,7 +150,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildVideoTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -172,7 +174,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildAudioTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -195,7 +198,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildVoiceTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -217,7 +221,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildFileTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -240,7 +245,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildNoticeTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -262,7 +268,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildEmoteTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -284,7 +291,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildLocationTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -306,7 +314,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildGalleryTimelineItem(for eventItemProxy: EventTimelineItemProxy,
@@ -329,7 +338,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildStickerTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
@@ -355,7 +365,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildPollTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
@@ -415,7 +426,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
reactions: buildAggregatedReactions(messageLikeContent.reactions),
deliveryStatus: eventItemProxy.deliveryStatus,
orderedReadReceipts: buildOrderedReadReceipts(eventItemProxy.readReceipts),
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState)))
encryptionAuthenticity: buildEncryptionAuthenticity(eventItemProxy.shieldState),
encryptionForwarder: eventItemProxy.forwarder))
}
private func buildRedactedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,