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:
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user