Reaction highlights (#1145)

* Highlight own reactions

- Highlight own reactions on timeline and improve style.
- Highlight reactions on message bottom sheet only when it our own.

* Improve overlay background colours

* pin to new sdk version

* Remove the red used for debug.

* Fix padding, overlap and only display count if > 1.

- Fix reaction padding and overlap of reaction and bubble.
- Only display count if greater than 1.
- Return EmptyView for TimelineDeliveryStatus.cancelled.

* Fix colors as per design recommendations.

* Add Snapshot tests for reactions and documentation.

- Add UITests for the timeline including reactions.
- Add the reference snapshots.
- Add documentation on how to run snapshot tests and update the reference snapshots.

* Fix Package.resolved
This commit is contained in:
David Langley
2023-06-25 13:24:48 +01:00
committed by GitHub
parent 41772b57eb
commit 13decc668a
12 changed files with 64 additions and 13 deletions
@@ -86,9 +86,8 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
}
private var messageBubbleWithReactions: some View {
// Figma has a spacing of -4 but it doesn't take into account
// the centre aligned stroke width so we use -5 here
VStack(alignment: alignment, spacing: -5) {
// Figma overlaps reactions by 3
VStack(alignment: alignment, spacing: -3) {
messageBubble
.accessibilityElement(children: .combine)
@@ -44,23 +44,36 @@ struct TimelineReactionButton: View {
HStack(spacing: 4) {
Text(reaction.key)
.font(.compound.bodyMD)
Text(String(reaction.count))
.font(.compound.bodyMD)
.foregroundColor(.compound.textSecondary)
if reaction.count > 1 {
Text(String(reaction.count))
.font(.compound.bodyMD)
.foregroundColor(textColor)
}
}
.padding(.vertical, 6)
.padding(.horizontal, 8)
.background(
backgroundShape
.strokeBorder(reaction.isHighlighted ? Color.compound.textSecondary : .compound.bgCanvasDefault, lineWidth: 2)
.background(reaction.isHighlighted ? Color.compound.textPrimary.opacity(0.1) : .compound._bgReactionButton, in: backgroundShape)
)
.background(backgroundShape.fill(overlayBackgroundColor))
.overlay(backgroundShape.strokeBorder(overlayBorderColor))
.padding(2)
.overlay(backgroundShape.strokeBorder(Color.compound.bgCanvasDefault, lineWidth: 2))
.accessibilityElement(children: .combine)
}
var backgroundShape: some InsettableShape {
RoundedRectangle(cornerRadius: 12, style: .continuous)
}
var textColor: Color {
reaction.isHighlighted ? Color.compound.textPrimary : .compound.textSecondary
}
var overlayBackgroundColor: Color {
reaction.isHighlighted ? Color.compound.bgSubtlePrimary : .compound.bgSubtleSecondary
}
var overlayBorderColor: Color {
reaction.isHighlighted ? Color.compound.borderInteractivePrimary : .clear
}
}
struct TimelineReactionView_Previews: PreviewProvider {
@@ -163,7 +163,8 @@ public struct TimelineItemMenu: View {
}
private func reactionBackgroundColor(for emoji: String) -> Color {
if item.properties.reactions.first(where: { $0.key == emoji }) != nil {
if let reaction = item.properties.reactions.first(where: { $0.key == emoji }),
reaction.isHighlighted {
return .compound.bgActionPrimaryRest
} else {
return .clear
@@ -322,7 +322,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
private func aggregateReactions(_ reactions: [Reaction]) -> [AggregatedReaction] {
reactions.map { reaction in
let isHighlighted = false // reaction.details.contains(where: { $0.sender.id == userID })
let isHighlighted = reaction.senders.contains(where: { $0 == userID })
return AggregatedReaction(key: reaction.key, count: Int(reaction.count), isHighlighted: isHighlighted)
}
}
@@ -198,6 +198,17 @@ class MockScreen: Identifiable {
let coordinator = RoomScreenCoordinator(parameters: parameters)
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .roomSmallTimelineWithReactions:
let navigationStackCoordinator = NavigationStackCoordinator()
let timelineController = MockRoomTimelineController()
timelineController.timelineItems = RoomTimelineItemFixtures.default
let parameters = RoomScreenCoordinatorParameters(roomProxy: RoomProxyMock(with: .init(displayName: "New room", avatarURL: URL.picturesDirectory)),
timelineController: timelineController,
mediaProvider: MockMediaProvider(),
emojiProvider: EmojiProvider())
let coordinator = RoomScreenCoordinator(parameters: parameters)
navigationStackCoordinator.setRootCoordinator(coordinator)
return navigationStackCoordinator
case .roomSmallTimelineWithReadReceipts:
ServiceLocator.shared.settings.readReceiptsEnabled = true
let navigationStackCoordinator = NavigationStackCoordinator()
@@ -36,6 +36,7 @@ enum UITestsScreenIdentifier: String {
case roomPlainNoAvatar
case roomEncryptedWithAvatar
case roomSmallTimeline
case roomSmallTimelineWithReactions
case roomSmallTimelineWithReadReceipts
case roomSmallTimelineIncomingAndSmallPagination
case roomSmallTimelineLargePagination