Add reaction toggles to macOS timeline context menu. (#2801)

Highlight reactions that have already been sent.

Share the default reactions.
This commit is contained in:
Doug
2024-05-07 16:50:54 +01:00
committed by GitHub
parent 2c77654440
commit 749485d55d
3 changed files with 62 additions and 7 deletions

View File

@@ -257,6 +257,9 @@ class RoomScreenInteractionHandler {
actionsSubject.send(.displayReportContent(itemID: itemID, senderID: eventTimelineItem.sender.id))
case .react:
showEmojiPicker(for: itemID)
case .toggleReaction(let key):
guard let eventID = itemID.eventID else { return }
Task { await roomProxy.timeline.toggleReaction(key, to: eventID) }
case .endPoll(let pollStartID):
endPoll(pollStartID: pollStartID)
}

View File

@@ -14,6 +14,8 @@
// limitations under the License.
//
import Compound
import SFSafeSymbols
import SwiftUI
/// The contents of the context menu shown when right clicking an item in the timeline on a Mac
@@ -27,8 +29,24 @@ struct TimelineItemMacContextMenu: View {
if let menuActions = actionProvider?(item.id) {
Section {
if item.isReactable {
Button { send(.react) } label: {
TimelineItemMenuAction.react.label
if #available(iOS 17.0, *) {
let reactions = (item as? EventBasedTimelineItemProtocol)?.properties.reactions ?? []
ControlGroup {
ForEach(menuActions.reactions, id: \.key) {
ReactionToggle(reaction: $0, reactions: reactions) {
send(.toggleReaction(key: $0))
}
}
Button { send(.react) } label: {
CompoundIcon(\.reactionAdd)
}
}
.controlGroupStyle(.palette)
} else {
Button { send(.react) } label: {
TimelineItemMenuAction.react.label
}
}
}
@@ -54,3 +72,21 @@ struct TimelineItemMacContextMenu: View {
}
}
}
/// A button that acts as a toggle for reacting to a message.
private struct ReactionToggle: View {
let reaction: TimelineItemMenuReaction
let reactions: [AggregatedReaction]
let action: (String) -> Void
var isOn: Bool {
reactions.contains { $0.key == reaction.key && $0.isHighlighted }
}
var body: some View {
Button { action(reaction.key) } label: {
Image(systemSymbol: reaction.symbol)
.symbolVariant(isOn ? .fill : .none)
}
}
}

View File

@@ -15,9 +15,11 @@
//
import Compound
import SFSafeSymbols
import SwiftUI
struct TimelineItemMenuActions {
let reactions: [TimelineItemMenuReaction]
let actions: [TimelineItemMenuAction]
let debugActions: [TimelineItemMenuAction]
@@ -28,6 +30,13 @@ struct TimelineItemMenuActions {
self.actions = actions
self.debugActions = debugActions
reactions = [
.init(key: "👍️", symbol: .handThumbsup),
.init(key: "👎️", symbol: .handThumbsdown),
.init(key: "🔥", symbol: .flame),
.init(key: "❤️", symbol: .heart),
.init(key: "👏", symbol: .handsClap)
]
}
var canReply: Bool {
@@ -41,6 +50,11 @@ struct TimelineItemMenuActions {
}
}
struct TimelineItemMenuReaction {
let key: String
let symbol: SFSymbol
}
enum TimelineItemMenuAction: Identifiable, Hashable {
case copy
case edit
@@ -52,6 +66,7 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
case retryDecryption(sessionID: String)
case report
case react
case toggleReaction(key: String)
case endPoll(pollStartID: String)
var id: Self { self }
@@ -120,6 +135,9 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
Label(L10n.actionReportContent, icon: \.chatProblem)
case .react:
Label(L10n.actionReact, icon: \.reactionAdd)
case .toggleReaction:
// Unused label - manually created in TimelineItemMacContextMenu.
Label(L10n.actionReact, icon: \.reactionAdd)
case .endPoll:
Label(L10n.actionEndPoll, icon: \.pollsEnd)
}
@@ -216,11 +234,9 @@ struct TimelineItemMenu: View {
private var reactionsSection: some View {
ScrollView(.horizontal) {
HStack(alignment: .center, spacing: 8) {
reactionButton(for: "👍️")
reactionButton(for: "👎️")
reactionButton(for: "🔥")
reactionButton(for: "❤️")
reactionButton(for: "👏")
ForEach(actions.reactions, id: \.key) {
reactionButton(for: $0.key)
}
Button {
dismiss()