Add "reply in thread" timeline item action menu entry.

This commit is contained in:
Stefan Ceriu
2025-06-10 14:00:24 +03:00
committed by Stefan Ceriu
parent 09779a4e12
commit 3ef479305a
52 changed files with 109 additions and 88 deletions
@@ -146,6 +146,7 @@ class TimelineMediaPreviewViewModel: TimelineMediaPreviewViewModelType {
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
isDM: timelineContext.viewState.isDirectOneToOneRoom,
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
areThreadsEnabled: timelineContext.viewState.areThreadsEnabled,
timelineKind: timelineContext.viewState.timelineKind,
emojiProvider: timelineContext.viewState.emojiProvider)
.makeActions()
@@ -24,6 +24,7 @@ enum TimelineInteractionHandlerAction {
case displayErrorToast(String)
case viewInRoomTimeline(eventID: String)
case displayThread(itemID: TimelineItemIdentifier)
}
/// The interaction handler groups logic for dealing with various actions the user can take on a timeline's
@@ -162,6 +163,8 @@ class TimelineInteractionHandler {
let replyDetails = TimelineItemReplyDetails.loaded(sender: eventTimelineItem.sender, eventID: eventID, eventContent: replyInfo.type)
actionsSubject.send(.composer(action: .setMode(mode: .reply(eventID: eventID, replyDetails: replyDetails, isThread: replyInfo.isThread))))
case .replyInThread:
actionsSubject.send(.displayThread(itemID: eventTimelineItem.id))
case .forward(let itemID):
actionsSubject.send(.displayMessageForwarding(itemID: itemID))
case .viewSource:
@@ -517,6 +517,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
state.bindings.debugInfo = debugInfo
case .viewInRoomTimeline(let eventID):
actionsSubject.send(.viewInRoomTimeline(eventID: eventID))
case .displayThread(let itemID):
actionsSubject.send(.displayThread(itemID: itemID))
}
}
.store(in: &cancellables)
@@ -531,6 +533,10 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol {
.weakAssign(to: \.state.isViewSourceEnabled, on: self)
.store(in: &cancellables)
appSettings.$threadsEnabled
.weakAssign(to: \.state.areThreadsEnabled, on: self)
.store(in: &cancellables)
clientProxy.timelineMediaVisibilityPublisher
.removeDuplicates()
.flatMap { [weak self] timelineMediaVisibility -> AnyPublisher<Bool, Never> in
@@ -344,6 +344,7 @@ struct TimelineItemMenu_Previews: PreviewProvider, TestablePreview {
pinnedEventIDs: [],
isDM: true,
isViewSourceEnabled: true,
areThreadsEnabled: true,
timelineKind: .live,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings))
guard let actions = provider.makeActions() else { return nil }
@@ -65,6 +65,7 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
case copyPermalink
case redact
case reply(isThread: Bool)
case replyInThread
case forward(itemID: TimelineItemIdentifier)
case viewSource
case report
@@ -159,6 +160,8 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
Label(L10n.actionCopyLinkToMessage, icon: \.link)
case .reply(let isThread):
Label(isThread ? L10n.actionReplyInThread : L10n.actionReply, icon: \.reply)
case .replyInThread:
Label(L10n.actionReplyInThread, icon: \.threads)
case .forward:
Label(L10n.actionForward, icon: \.forward)
case .redact:
@@ -16,6 +16,7 @@ struct TimelineItemMenuActionProvider {
let pinnedEventIDs: Set<String>
let isDM: Bool
let isViewSourceEnabled: Bool
let areThreadsEnabled: Bool
let timelineKind: TimelineKind
let emojiProvider: EmojiProviderProtocol
@@ -52,6 +53,10 @@ struct TimelineItemMenuActionProvider {
} else {
actions.append(.reply(isThread: false))
}
if areThreadsEnabled, !timelineKind.isThread {
actions.append(.replyInThread)
}
}
if item.isForwardable {
@@ -156,6 +156,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
pinnedEventIDs: context.viewState.pinnedEventIDs,
isDM: context.viewState.isDirectOneToOneRoom,
isViewSourceEnabled: context.viewState.isViewSourceEnabled,
areThreadsEnabled: context.viewState.areThreadsEnabled,
timelineKind: context.viewState.timelineKind,
emojiProvider: context.viewState.emojiProvider)
TimelineItemMacContextMenu(item: timelineItem, actionProvider: provider) { action in
@@ -33,6 +33,7 @@ struct TimelineView: View {
pinnedEventIDs: timelineContext.viewState.pinnedEventIDs,
isDM: timelineContext.viewState.isDirectOneToOneRoom,
isViewSourceEnabled: timelineContext.viewState.isViewSourceEnabled,
areThreadsEnabled: timelineContext.viewState.areThreadsEnabled,
timelineKind: timelineContext.viewState.timelineKind,
emojiProvider: timelineContext.viewState.emojiProvider)
.makeActions()