mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:02:10 +00:00
Setup composer actions within threaded timelines.
This commit is contained in:
committed by
Stefan Ceriu
parent
c3a9e5deaa
commit
21e07e164c
@@ -967,6 +967,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
|
||||
|
||||
switch await roomProxy.timeline.sendMessage(replyText,
|
||||
html: nil,
|
||||
threadRootEventID: nil,
|
||||
inReplyToEventID: nil,
|
||||
intentionalMentions: .empty) {
|
||||
case .success:
|
||||
|
||||
@@ -16683,15 +16683,15 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
//MARK: - sendMessage
|
||||
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = 0
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount: Int {
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = 0
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount
|
||||
return sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount
|
||||
returnValue = sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@@ -16699,29 +16699,29 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue
|
||||
sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue
|
||||
sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsCalled: Bool {
|
||||
return sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount > 0
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsCalled: Bool {
|
||||
return sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsCallsCount > 0
|
||||
}
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedArguments: (message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)?
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedInvocations: [(message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)] = []
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsReceivedArguments: (message: String, html: String?, threadRootEventID: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)?
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsReceivedInvocations: [(message: String, html: String?, threadRootEventID: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions)] = []
|
||||
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsReturnValue: Result<Void, TimelineProxyError>! {
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingReturnValue: Result<Void, TimelineProxyError>!
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsReturnValue: Result<Void, TimelineProxyError>! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue
|
||||
return sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: Result<Void, TimelineProxyError>? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue
|
||||
returnValue = sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@@ -16729,26 +16729,26 @@ class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue
|
||||
sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
sendMessageHtmlInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue
|
||||
sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure: ((String, String?, String?, IntentionalMentions) async -> Result<Void, TimelineProxyError>)?
|
||||
var sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsClosure: ((String, String?, String?, String?, IntentionalMentions) async -> Result<Void, TimelineProxyError>)?
|
||||
|
||||
func sendMessage(_ message: String, html: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageHtmlInReplyToEventIDIntentionalMentionsCallsCount += 1
|
||||
sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedArguments = (message: message, html: html, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions)
|
||||
func sendMessage(_ message: String, html: String?, threadRootEventID: String?, inReplyToEventID: String?, intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsCallsCount += 1
|
||||
sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsReceivedArguments = (message: message, html: html, threadRootEventID: threadRootEventID, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions)
|
||||
DispatchQueue.main.async {
|
||||
self.sendMessageHtmlInReplyToEventIDIntentionalMentionsReceivedInvocations.append((message: message, html: html, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions))
|
||||
self.sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsReceivedInvocations.append((message: message, html: html, threadRootEventID: threadRootEventID, inReplyToEventID: inReplyToEventID, intentionalMentions: intentionalMentions))
|
||||
}
|
||||
if let sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure = sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure {
|
||||
return await sendMessageHtmlInReplyToEventIDIntentionalMentionsClosure(message, html, inReplyToEventID, intentionalMentions)
|
||||
if let sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsClosure = sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsClosure {
|
||||
return await sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsClosure(message, html, threadRootEventID, inReplyToEventID, intentionalMentions)
|
||||
} else {
|
||||
return sendMessageHtmlInReplyToEventIDIntentionalMentionsReturnValue
|
||||
return sendMessageHtmlThreadRootEventIDInReplyToEventIDIntentionalMentionsReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - toggleReaction
|
||||
|
||||
@@ -120,9 +120,11 @@ final class ThreadTimelineScreenCoordinator: CoordinatorProtocol {
|
||||
actionsSubject.send(.presentLocationViewer(body: body, geoURI: geoURI, description: description))
|
||||
case .displayResolveSendFailure(let failure, let sendHandle):
|
||||
actionsSubject.send(.presentResolveSendFailure(failure: failure, sendHandle: sendHandle))
|
||||
case .displayThread, .composer, .hasScrolled, .displayRoom:
|
||||
case .hasScrolled, .displayRoom:
|
||||
break
|
||||
case .viewInRoomTimeline:
|
||||
case .composer(let action):
|
||||
composerViewModel.process(timelineAction: action)
|
||||
case .viewInRoomTimeline, .displayThread:
|
||||
fatalError("The action: \(action) should not be sent to this coordinator")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ struct TimelineItemMenuActionProvider {
|
||||
secondaryActions = secondaryActions.filter(\.canAppearInRedacted)
|
||||
}
|
||||
|
||||
let isReactable = timelineKind == .live || timelineKind == .detached || timelineKind == .thread ? item.isReactable : false
|
||||
let isReactable = timelineKind == .live || timelineKind == .detached || timelineKind.isThread ? item.isReactable : false
|
||||
|
||||
return .init(isReactable: isReactable, actions: actions, secondaryActions: secondaryActions, emojiProvider: emojiProvider)
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
.onTapGesture { }
|
||||
}
|
||||
|
||||
if context.viewState.timelineKind != .thread, let threadSummary = timelineItem.properties.threadSummary {
|
||||
if !context.viewState.timelineKind.isThread, let threadSummary = timelineItem.properties.threadSummary {
|
||||
TimelineThreadSummaryView(threadSummary: threadSummary) {
|
||||
context.send(viewAction: .displayThread(itemID: timelineItem.id))
|
||||
}
|
||||
@@ -177,7 +177,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
@ViewBuilder
|
||||
var contentWithReply: some View {
|
||||
TimelineBubbleLayout(spacing: 8) {
|
||||
if context.viewState.timelineKind != .thread, timelineItem.properties.isThreaded {
|
||||
if !context.viewState.timelineKind.isThread, timelineItem.properties.isThreaded {
|
||||
ThreadDecorator()
|
||||
.padding(.leading, 4)
|
||||
.layoutPriority(TimelineBubbleLayout.Priority.regularText)
|
||||
@@ -213,7 +213,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
||||
}
|
||||
|
||||
private var shouldShowReplyDetails: Bool {
|
||||
!timelineItem.properties.isThreaded || (timelineItem.properties.isThreaded && context.viewState.timelineKind != .thread)
|
||||
!timelineItem.properties.isThreaded || (timelineItem.properties.isThreaded && !context.viewState.timelineKind.isThread)
|
||||
}
|
||||
|
||||
private var messageBubbleTopPadding: CGFloat {
|
||||
|
||||
@@ -175,7 +175,7 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol {
|
||||
trackReadReceipts: true,
|
||||
reportUtds: true))
|
||||
|
||||
let timeline = TimelineProxy(timeline: sdkTimeline, kind: .thread)
|
||||
let timeline = TimelineProxy(timeline: sdkTimeline, kind: .thread(rootEventID: eventID))
|
||||
await timeline.subscribeForUpdates()
|
||||
|
||||
return .success(timeline)
|
||||
|
||||
@@ -158,6 +158,7 @@ class TimelineController: TimelineControllerProtocol {
|
||||
|
||||
switch await activeTimeline.sendMessage(message,
|
||||
html: html,
|
||||
threadRootEventID: timelineKind.threadRootEventID,
|
||||
inReplyToEventID: inReplyToEventID,
|
||||
intentionalMentions: intentionalMentions) {
|
||||
case .success:
|
||||
|
||||
@@ -389,8 +389,15 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
return .success(())
|
||||
}
|
||||
|
||||
/// Send a message within a room. If `inReplyToEventID` is specified then it will be sent as a reply
|
||||
/// to that particular message. If the `threadRootEventID` is also specified then it will be sent
|
||||
/// as a reply to the given `inReplyToEventID` within the thread rooted in `threadRootEventID`
|
||||
///
|
||||
/// Internally `enforceThread` is set to true whenever `threadRootEventID` is specified
|
||||
/// and `replyWithinThread` when `inReplyToEventID` is.
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
threadRootEventID: String?,
|
||||
inReplyToEventID: String? = nil,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
|
||||
if let inReplyToEventID {
|
||||
@@ -405,14 +412,18 @@ final class TimelineProxy: TimelineProxyProtocol {
|
||||
|
||||
do {
|
||||
if let inReplyToEventID {
|
||||
// `enforceThread` will force send the message a thread with `inReplyToEventID` while
|
||||
// `replyWithinThread` will create an in-reply-to associated field *within* that same thread
|
||||
try await timeline.sendReply(msg: messageContent, replyParams: .init(eventId: inReplyToEventID,
|
||||
enforceThread: false,
|
||||
replyWithinThread: false))
|
||||
enforceThread: threadRootEventID != nil,
|
||||
replyWithinThread: threadRootEventID != nil))
|
||||
MXLog.info("Finished sending reply to eventID: \(inReplyToEventID)")
|
||||
} else {
|
||||
_ = try await timeline.send(msg: messageContent)
|
||||
if let threadRootEventID {
|
||||
try await timeline.sendReply(msg: messageContent, replyParams: .init(eventId: threadRootEventID,
|
||||
enforceThread: true,
|
||||
replyWithinThread: false))
|
||||
} else {
|
||||
_ = try await timeline.send(msg: messageContent)
|
||||
}
|
||||
MXLog.info("Finished sending message")
|
||||
}
|
||||
} catch {
|
||||
|
||||
@@ -13,10 +13,23 @@ enum TimelineKind: Equatable {
|
||||
case live
|
||||
case detached
|
||||
case pinned
|
||||
case thread
|
||||
case thread(rootEventID: String)
|
||||
|
||||
enum MediaPresentation { case roomScreenLive, roomScreenDetached, pinnedEventsScreen, mediaFilesScreen }
|
||||
case media(MediaPresentation)
|
||||
|
||||
var isThread: Bool {
|
||||
threadRootEventID != nil
|
||||
}
|
||||
|
||||
var threadRootEventID: String? {
|
||||
switch self {
|
||||
case .thread(let rootEventID):
|
||||
rootEventID
|
||||
default:
|
||||
nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TimelineFocus {
|
||||
@@ -105,6 +118,7 @@ protocol TimelineProxyProtocol {
|
||||
|
||||
func sendMessage(_ message: String,
|
||||
html: String?,
|
||||
threadRootEventID: String?,
|
||||
inReplyToEventID: String?,
|
||||
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user