mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 11:58:32 +00:00
7e1476d973
* Add support for editing media captions. * Add composer snapshots. * PR comment.
172 lines
5.1 KiB
Swift
172 lines
5.1 KiB
Swift
//
|
|
// Copyright 2024 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// Please see LICENSE in the repository root for full details.
|
|
//
|
|
|
|
import OrderedCollections
|
|
import SFSafeSymbols
|
|
import SwiftUI
|
|
|
|
@MainActor
|
|
struct TimelineItemMenuActions {
|
|
let reactions: [TimelineItemMenuReaction]
|
|
let actions: [TimelineItemMenuAction]
|
|
let debugActions: [TimelineItemMenuAction]
|
|
|
|
init?(isReactable: Bool,
|
|
actions: [TimelineItemMenuAction],
|
|
debugActions: [TimelineItemMenuAction],
|
|
emojiProvider: EmojiProviderProtocol) {
|
|
if !isReactable, actions.isEmpty, debugActions.isEmpty {
|
|
return nil
|
|
}
|
|
|
|
self.actions = actions
|
|
self.debugActions = debugActions
|
|
|
|
var frequentlyUsed: OrderedSet<TimelineItemMenuReaction> = [
|
|
.init(key: "👍️", symbol: .handThumbsup),
|
|
.init(key: "👎️", symbol: .handThumbsdown),
|
|
.init(key: "🎉", symbol: .partyPopper),
|
|
.init(key: "❤️", symbol: .heart)
|
|
]
|
|
|
|
frequentlyUsed.append(contentsOf: emojiProvider.frequentlyUsedSystemEmojis().map { TimelineItemMenuReaction(key: $0, symbol: .heart) })
|
|
|
|
reactions = if isReactable {
|
|
Array(frequentlyUsed.elements.prefix(10))
|
|
} else {
|
|
[]
|
|
}
|
|
}
|
|
}
|
|
|
|
struct TimelineItemMenuReaction: Hashable {
|
|
let key: String
|
|
let symbol: SFSymbol
|
|
|
|
// Frequently used emojis on the all use the same .heart SFSymbol.
|
|
// Override equatable so we can remove duplicates.
|
|
static func == (lhs: TimelineItemMenuReaction, rhs: TimelineItemMenuReaction) -> Bool {
|
|
lhs.key == rhs.key
|
|
}
|
|
}
|
|
|
|
enum TimelineItemMenuAction: Identifiable, Hashable {
|
|
case copy
|
|
case edit
|
|
case addCaption
|
|
case editCaption
|
|
case removeCaption
|
|
case copyPermalink
|
|
case redact
|
|
case reply(isThread: Bool)
|
|
case forward(itemID: TimelineItemIdentifier)
|
|
case viewSource
|
|
case retryDecryption(sessionID: String)
|
|
case report
|
|
case react
|
|
case toggleReaction(key: String)
|
|
case endPoll(pollStartID: String)
|
|
case pin
|
|
case unpin
|
|
case viewInRoomTimeline
|
|
|
|
var id: Self { self }
|
|
|
|
/// Whether the item should cancel a reply/edit occurring in the composer.
|
|
var switchToDefaultComposer: Bool {
|
|
switch self {
|
|
case .reply, .edit, .addCaption, .editCaption:
|
|
return false
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
/// Whether the action should be shown for an item that failed to send.
|
|
var canAppearInFailedEcho: Bool {
|
|
switch self {
|
|
case .copy, .edit, .redact, .viewSource:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Whether the action should be shown for a redacted item.
|
|
var canAppearInRedacted: Bool {
|
|
switch self {
|
|
case .viewSource, .unpin, .viewInRoomTimeline:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// Whether or not the action is destructive.
|
|
var isDestructive: Bool {
|
|
switch self {
|
|
case .redact, .report, .removeCaption:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
var canAppearInPinnedEventsTimeline: Bool {
|
|
switch self {
|
|
case .viewInRoomTimeline, .pin, .unpin, .forward:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
/// The action's label.
|
|
@ViewBuilder
|
|
var label: some View {
|
|
switch self {
|
|
case .copy:
|
|
Label(L10n.actionCopy, icon: \.copy)
|
|
case .edit:
|
|
Label(L10n.actionEdit, icon: \.edit)
|
|
case .addCaption:
|
|
Label(L10n.actionAddCaption, icon: \.edit)
|
|
case .editCaption:
|
|
Label(L10n.actionEditCaption, icon: \.edit)
|
|
case .removeCaption:
|
|
Label(L10n.actionRemoveCaption, icon: \.delete)
|
|
case .copyPermalink:
|
|
Label(L10n.actionCopyLinkToMessage, icon: \.link)
|
|
case .reply(let isThread):
|
|
Label(isThread ? L10n.actionReplyInThread : L10n.actionReply, icon: \.reply)
|
|
case .forward:
|
|
Label(L10n.actionForward, icon: \.forward)
|
|
case .redact:
|
|
Label(L10n.actionRemove, icon: \.delete)
|
|
case .viewSource:
|
|
Label(L10n.actionViewSource, icon: \.code)
|
|
case .retryDecryption:
|
|
Label(L10n.actionRetryDecryption, systemImage: "arrow.down.message")
|
|
case .report:
|
|
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)
|
|
case .pin:
|
|
Label(L10n.actionPin, icon: \.pin)
|
|
case .unpin:
|
|
Label(L10n.actionUnpin, icon: \.unpin)
|
|
case .viewInRoomTimeline:
|
|
Label(L10n.actionViewInTimeline, icon: \.visibilityOn)
|
|
}
|
|
}
|
|
}
|