mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
element-hq/element-x-ios/issues/2636 - Expose paths for focusing replied-to timeline items by tapping on an in-reply-to message bubble
This commit is contained in:
committed by
Stefan Ceriu
parent
43c132720d
commit
83c620d685
@@ -199,18 +199,32 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview {
|
|||||||
static let viewModel = RoomScreenViewModel.mock
|
static let viewModel = RoomScreenViewModel.mock
|
||||||
|
|
||||||
static let replyTypes: [TimelineItemReplyDetails] = [
|
static let replyTypes: [TimelineItemReplyDetails] = [
|
||||||
.loaded(sender: .init(id: "Dave"), eventContent: .message(.audio(.init(body: "Audio: Ride the lightning", duration: 100, waveform: nil, source: nil, contentType: nil)))),
|
.loaded(sender: .init(id: "Dave"),
|
||||||
.loaded(sender: .init(id: "James"), eventContent: .message(.emote(.init(body: "Emote: James thinks he's the phantom lord")))),
|
eventID: "123",
|
||||||
.loaded(sender: .init(id: "Robert"), eventContent: .message(.file(.init(body: "File: Crash course in brain surgery.pdf", source: nil, thumbnailSource: nil, contentType: nil)))),
|
eventContent: .message(.audio(.init(body: "Audio: Ride the lightning", duration: 100, waveform: nil, source: nil, contentType: nil)))),
|
||||||
.loaded(sender: .init(id: "Cliff"), eventContent: .message(.image(.init(body: "Image: Pushead",
|
.loaded(sender: .init(id: "James"),
|
||||||
source: .init(url: .picturesDirectory, mimeType: nil),
|
eventID: "123",
|
||||||
thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))),
|
eventContent: .message(.emote(.init(body: "Emote: James thinks he's the phantom lord")))),
|
||||||
.loaded(sender: .init(id: "Jason"), eventContent: .message(.notice(.init(body: "Notice: Too far gone?")))),
|
.loaded(sender: .init(id: "Robert"),
|
||||||
.loaded(sender: .init(id: "Kirk"), eventContent: .message(.text(.init(body: "Text: Where the wild things are")))),
|
eventID: "123",
|
||||||
.loaded(sender: .init(id: "Lars"), eventContent: .message(.video(.init(body: "Video: Through the never",
|
eventContent: .message(.file(.init(body: "File: Crash course in brain surgery.pdf", source: nil, thumbnailSource: nil, contentType: nil)))),
|
||||||
duration: 100,
|
.loaded(sender: .init(id: "Cliff"),
|
||||||
source: nil,
|
eventID: "123",
|
||||||
thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))),
|
eventContent: .message(.image(.init(body: "Image: Pushead",
|
||||||
|
source: .init(url: .picturesDirectory, mimeType: nil),
|
||||||
|
thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))),
|
||||||
|
.loaded(sender: .init(id: "Jason"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.notice(.init(body: "Notice: Too far gone?")))),
|
||||||
|
.loaded(sender: .init(id: "Kirk"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Text: Where the wild things are")))),
|
||||||
|
.loaded(sender: .init(id: "Lars"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.video(.init(body: "Video: Through the never",
|
||||||
|
duration: 100,
|
||||||
|
source: nil,
|
||||||
|
thumbnailSource: .init(url: .picturesDirectory, mimeType: nil))))),
|
||||||
.loading(eventID: "")
|
.loading(eventID: "")
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -247,6 +261,7 @@ struct MessageComposer_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
messageComposer(mode: .reply(itemID: .random,
|
messageComposer(mode: .reply(itemID: .random,
|
||||||
replyDetails: .loaded(sender: .init(id: "Kirk"),
|
replyDetails: .loaded(sender: .init(id: "Kirk"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Text: Where the wild things are")))),
|
eventContent: .message(.text(.init(body: "Text: Where the wild things are")))),
|
||||||
isThread: false))
|
isThread: false))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,8 +230,12 @@ class RoomScreenInteractionHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case .reply:
|
case .reply:
|
||||||
|
guard let eventID = eventTimelineItem.id.eventID else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let replyInfo = buildReplyInfo(for: eventTimelineItem)
|
let replyInfo = buildReplyInfo(for: eventTimelineItem)
|
||||||
let replyDetails = TimelineItemReplyDetails.loaded(sender: eventTimelineItem.sender, eventContent: replyInfo.type)
|
let replyDetails = TimelineItemReplyDetails.loaded(sender: eventTimelineItem.sender, eventID: eventID, eventContent: replyInfo.type)
|
||||||
|
|
||||||
actionsSubject.send(.composer(action: .setMode(mode: .reply(itemID: eventTimelineItem.id, replyDetails: replyDetails, isThread: replyInfo.isThread))))
|
actionsSubject.send(.composer(action: .setMode(mode: .reply(itemID: eventTimelineItem.id, replyDetails: replyDetails, isThread: replyInfo.isThread))))
|
||||||
case .forward(let itemID):
|
case .forward(let itemID):
|
||||||
|
|||||||
@@ -76,8 +76,10 @@ enum RoomScreenViewAudioAction {
|
|||||||
|
|
||||||
enum RoomScreenViewAction {
|
enum RoomScreenViewAction {
|
||||||
case displayRoomDetails
|
case displayRoomDetails
|
||||||
|
|
||||||
case itemAppeared(itemID: TimelineItemIdentifier)
|
case itemAppeared(itemID: TimelineItemIdentifier)
|
||||||
case itemDisappeared(itemID: TimelineItemIdentifier)
|
case itemDisappeared(itemID: TimelineItemIdentifier)
|
||||||
|
|
||||||
case itemTapped(itemID: TimelineItemIdentifier)
|
case itemTapped(itemID: TimelineItemIdentifier)
|
||||||
case toggleReaction(key: String, itemID: TimelineItemIdentifier)
|
case toggleReaction(key: String, itemID: TimelineItemIdentifier)
|
||||||
case sendReadReceiptIfNeeded(TimelineItemIdentifier)
|
case sendReadReceiptIfNeeded(TimelineItemIdentifier)
|
||||||
@@ -104,6 +106,8 @@ enum RoomScreenViewAction {
|
|||||||
case audio(RoomScreenViewAudioAction)
|
case audio(RoomScreenViewAudioAction)
|
||||||
|
|
||||||
case presentCall
|
case presentCall
|
||||||
|
|
||||||
|
case focusOnEventID(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RoomScreenComposerAction {
|
enum RoomScreenComposerAction {
|
||||||
|
|||||||
@@ -172,6 +172,9 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
actionsSubject.send(.displayCallScreen)
|
actionsSubject.send(.displayCallScreen)
|
||||||
case .showReadReceipts(itemID: let itemID):
|
case .showReadReceipts(itemID: let itemID):
|
||||||
showReadReceipts(for: itemID)
|
showReadReceipts(for: itemID)
|
||||||
|
case .focusOnEventID(let eventID):
|
||||||
|
// TODO: .. something
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ struct TimelineReplyView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
if let timelineItemReplyDetails {
|
if let timelineItemReplyDetails {
|
||||||
switch timelineItemReplyDetails {
|
switch timelineItemReplyDetails {
|
||||||
case .loaded(let sender, let content):
|
case .loaded(let sender, _, let content):
|
||||||
switch content {
|
switch content {
|
||||||
case .message(let content):
|
case .message(let content):
|
||||||
switch content {
|
switch content {
|
||||||
@@ -131,38 +131,6 @@ struct TimelineReplyView: View {
|
|||||||
|
|
||||||
var icon: Icon?
|
var icon: Icon?
|
||||||
|
|
||||||
var isTextOnly: Bool {
|
|
||||||
icon == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The string shown as the message preview.
|
|
||||||
///
|
|
||||||
/// This converts the formatted body to a plain string to remove formatting
|
|
||||||
/// and render with a consistent font size. This conversion is done to avoid
|
|
||||||
/// showing markdown characters in the preview for messages with formatting.
|
|
||||||
var messagePreview: String {
|
|
||||||
guard let formattedBody,
|
|
||||||
let attributedString = try? NSMutableAttributedString(formattedBody, including: \.elementX) else {
|
|
||||||
return plainBody
|
|
||||||
}
|
|
||||||
|
|
||||||
let range = NSRange(location: 0, length: attributedString.length)
|
|
||||||
attributedString.enumerateAttributes(in: range) { attributes, range, _ in
|
|
||||||
if let userID = attributes[.MatrixUserID] as? String {
|
|
||||||
if let displayName = context.viewState.members[userID]?.displayName {
|
|
||||||
attributedString.replaceCharacters(in: range, with: "@\(displayName)")
|
|
||||||
} else {
|
|
||||||
attributedString.replaceCharacters(in: range, with: userID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if attributes[.MatrixAllUsersMention] as? Bool == true {
|
|
||||||
attributedString.replaceCharacters(in: range, with: PillConstants.atRoom)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return attributedString.string
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 8) {
|
HStack(spacing: 8) {
|
||||||
iconView
|
iconView
|
||||||
@@ -183,7 +151,7 @@ struct TimelineReplyView: View {
|
|||||||
.tint(.compound.textLinkExternal)
|
.tint(.compound.textLinkExternal)
|
||||||
.lineLimit(2)
|
.lineLimit(2)
|
||||||
}
|
}
|
||||||
.padding(.leading, isTextOnly ? 8 : 0)
|
.padding(.leading, icon == nil ? 8 : 0)
|
||||||
.padding(.trailing, 8)
|
.padding(.trailing, 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,6 +184,34 @@ struct TimelineReplyView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The string shown as the message preview.
|
||||||
|
///
|
||||||
|
/// This converts the formatted body to a plain string to remove formatting
|
||||||
|
/// and render with a consistent font size. This conversion is done to avoid
|
||||||
|
/// showing markdown characters in the preview for messages with formatting.
|
||||||
|
private var messagePreview: String {
|
||||||
|
guard let formattedBody,
|
||||||
|
let attributedString = try? NSMutableAttributedString(formattedBody, including: \.elementX) else {
|
||||||
|
return plainBody
|
||||||
|
}
|
||||||
|
|
||||||
|
let range = NSRange(location: 0, length: attributedString.length)
|
||||||
|
attributedString.enumerateAttributes(in: range) { attributes, range, _ in
|
||||||
|
if let userID = attributes[.MatrixUserID] as? String {
|
||||||
|
if let displayName = context.viewState.members[userID]?.displayName {
|
||||||
|
attributedString.replaceCharacters(in: range, with: "@\(displayName)")
|
||||||
|
} else {
|
||||||
|
attributedString.replaceCharacters(in: range, with: userID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if attributes[.MatrixAllUsersMention] as? Bool == true {
|
||||||
|
attributedString.replaceCharacters(in: range, with: PillConstants.atRoom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attributedString.string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -244,18 +240,22 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "This is a reply"))))),
|
eventContent: .message(.text(.init(body: "This is a reply"))))),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.emote(.init(body: "says hello"))))),
|
eventContent: .message(.emote(.init(body: "says hello"))))),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.notice(.init(body: "Hello world"))))),
|
eventContent: .message(.notice(.init(body: "Hello world"))))),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.audio(.init(body: "Some audio",
|
eventContent: .message(.audio(.init(body: "Some audio",
|
||||||
duration: 0,
|
duration: 0,
|
||||||
waveform: nil,
|
waveform: nil,
|
||||||
@@ -264,6 +264,7 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.file(.init(body: "Some file",
|
eventContent: .message(.file(.init(body: "Some file",
|
||||||
source: nil,
|
source: nil,
|
||||||
thumbnailSource: nil,
|
thumbnailSource: nil,
|
||||||
@@ -271,22 +272,26 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
|
|||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.image(.init(body: "Some image",
|
eventContent: .message(.image(.init(body: "Some image",
|
||||||
source: imageSource,
|
source: imageSource,
|
||||||
thumbnailSource: imageSource))))),
|
thumbnailSource: imageSource))))),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.video(.init(body: "Some video",
|
eventContent: .message(.video(.init(body: "Some video",
|
||||||
duration: 0,
|
duration: 0,
|
||||||
source: nil,
|
source: nil,
|
||||||
thumbnailSource: imageSource))))),
|
thumbnailSource: imageSource))))),
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.location(.init(body: ""))))),
|
eventContent: .message(.location(.init(body: ""))))),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.voice(.init(body: "Some voice message",
|
eventContent: .message(.voice(.init(body: "Some voice message",
|
||||||
duration: 0,
|
duration: 0,
|
||||||
waveform: nil,
|
waveform: nil,
|
||||||
@@ -294,16 +299,20 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview {
|
|||||||
contentType: nil))))),
|
contentType: nil))))),
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithMention))))),
|
eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithMention))))),
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithAtRoomMention))))),
|
eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithAtRoomMention))))),
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .poll(question: "Do you like polls?"))),
|
eventContent: .poll(question: "Do you like polls?"))),
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline,
|
TimelineReplyView(placement: .timeline,
|
||||||
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .redacted))
|
eventContent: .redacted))
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,9 +220,11 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
|||||||
.padding(.leading, 4)
|
.padding(.leading, 4)
|
||||||
.layoutPriority(TimelineBubbleLayout.Priority.regularText)
|
.layoutPriority(TimelineBubbleLayout.Priority.regularText)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let replyDetails = messageTimelineItem.replyDetails {
|
if let replyDetails = messageTimelineItem.replyDetails {
|
||||||
// The rendered reply bubble with a greedy width. The custom layout prevents
|
// The rendered reply bubble with a greedy width. The custom layout prevents
|
||||||
// the infinite width from increasing the overall width of the view.
|
// the infinite width from increasing the overall width of the view.
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: replyDetails)
|
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: replyDetails)
|
||||||
.fixedSize(horizontal: false, vertical: true)
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
.padding(4.0)
|
.padding(4.0)
|
||||||
@@ -230,6 +232,9 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
|
|||||||
.background(Color.compound.bgCanvasDefault)
|
.background(Color.compound.bgCanvasDefault)
|
||||||
.cornerRadius(8)
|
.cornerRadius(8)
|
||||||
.layoutPriority(TimelineBubbleLayout.Priority.visibleQuote)
|
.layoutPriority(TimelineBubbleLayout.Priority.visibleQuote)
|
||||||
|
.onTapGesture {
|
||||||
|
context.send(viewAction: .focusOnEventID(replyDetails.eventID))
|
||||||
|
}
|
||||||
|
|
||||||
// Add a fixed width reply bubble that is used for layout calculations but won't be rendered.
|
// Add a fixed width reply bubble that is used for layout calculations but won't be rendered.
|
||||||
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: replyDetails)
|
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: replyDetails)
|
||||||
@@ -418,6 +423,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
sender: .init(id: "whoever"),
|
sender: .init(id: "whoever"),
|
||||||
content: .init(body: "A long message that should be on multiple lines."),
|
content: .init(body: "A long message that should be on multiple lines."),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))),
|
eventContent: .message(.text(.init(body: "Short"))))),
|
||||||
groupStyle: .single))
|
groupStyle: .single))
|
||||||
|
|
||||||
@@ -434,6 +440,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
source: nil,
|
source: nil,
|
||||||
contentType: nil),
|
contentType: nil),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
|
|
||||||
FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
||||||
@@ -448,6 +455,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
thumbnailSource: nil,
|
thumbnailSource: nil,
|
||||||
contentType: nil),
|
contentType: nil),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
||||||
timestamp: "10:42",
|
timestamp: "10:42",
|
||||||
@@ -458,6 +466,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
sender: .init(id: ""),
|
sender: .init(id: ""),
|
||||||
content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil),
|
content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
@@ -471,6 +480,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
longitude: 12.496366),
|
longitude: 12.496366),
|
||||||
description: "Location description description description description description description description description"),
|
description: "Location description description description description description description description description"),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
@@ -482,6 +492,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
content: .init(body: "Fallback geo uri description",
|
content: .init(body: "Fallback geo uri description",
|
||||||
geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil),
|
geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
|
|
||||||
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
||||||
@@ -497,6 +508,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
source: nil,
|
source: nil,
|
||||||
contentType: nil),
|
contentType: nil),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))),
|
eventContent: .message(.text(.init(body: "Short"))))),
|
||||||
playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), duration: 10, waveform: EstimatedWaveform.mockWaveform))
|
playerState: AudioPlayerState(id: .timelineItemIdentifier(.random), duration: 10, waveform: EstimatedWaveform.mockWaveform))
|
||||||
}
|
}
|
||||||
@@ -528,6 +540,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
sender: .init(id: "whoever"),
|
sender: .init(id: "whoever"),
|
||||||
content: .init(body: "A long message that should be on multiple lines."),
|
content: .init(body: "A long message that should be on multiple lines."),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "Short"))))),
|
eventContent: .message(.text(.init(body: "Short"))))),
|
||||||
groupStyle: .single))
|
groupStyle: .single))
|
||||||
|
|
||||||
@@ -540,6 +553,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview
|
|||||||
sender: .init(id: "whoever"),
|
sender: .init(id: "whoever"),
|
||||||
content: .init(body: "Short message"),
|
content: .init(body: "Short message"),
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "A long message that should be on more than 2 lines and so will be clipped by the layout."))))),
|
eventContent: .message(.text(.init(body: "A long message that should be on more than 2 lines and so will be clipped by the layout."))))),
|
||||||
groupStyle: .single))
|
groupStyle: .single))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,11 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
|||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(.compound.iconTertiary)
|
.foregroundColor(.compound.iconTertiary)
|
||||||
.frame(width: 4.0)
|
.frame(width: 4.0)
|
||||||
|
|
||||||
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: replyDetails)
|
TimelineReplyView(placement: .timeline, timelineItemReplyDetails: replyDetails)
|
||||||
|
.onTapGesture {
|
||||||
|
context.send(viewAction: .focusOnEventID(replyDetails.eventID))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,101 +146,6 @@ struct TimelineItemPlainStylerView<Content: View>: View {
|
|||||||
struct TimelineItemPlainStylerView_Previews: PreviewProvider, TestablePreview {
|
struct TimelineItemPlainStylerView_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = RoomScreenViewModel.mock
|
static let viewModel = RoomScreenViewModel.mock
|
||||||
|
|
||||||
// These akwats include a reply
|
|
||||||
static var threads: some View {
|
|
||||||
ScrollView {
|
|
||||||
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
|
|
||||||
timestamp: "10:42",
|
|
||||||
isOutgoing: true,
|
|
||||||
isEditable: false,
|
|
||||||
canBeRepliedTo: true,
|
|
||||||
isThreaded: true,
|
|
||||||
sender: .init(id: "whoever"),
|
|
||||||
content: .init(body: "A long message that should be on multiple lines."),
|
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
|
||||||
eventContent: .message(.text(.init(body: "Short"))))),
|
|
||||||
groupStyle: .single))
|
|
||||||
|
|
||||||
AudioRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
|
||||||
timestamp: "10:42",
|
|
||||||
isOutgoing: true,
|
|
||||||
isEditable: false,
|
|
||||||
canBeRepliedTo: true,
|
|
||||||
isThreaded: true,
|
|
||||||
sender: .init(id: ""),
|
|
||||||
content: .init(body: "audio.ogg",
|
|
||||||
duration: 100,
|
|
||||||
waveform: nil,
|
|
||||||
source: nil,
|
|
||||||
contentType: nil),
|
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
|
||||||
FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
|
||||||
timestamp: "10:42",
|
|
||||||
isOutgoing: false,
|
|
||||||
isEditable: false,
|
|
||||||
canBeRepliedTo: true,
|
|
||||||
isThreaded: true,
|
|
||||||
sender: .init(id: ""),
|
|
||||||
content: .init(body: "File",
|
|
||||||
source: nil,
|
|
||||||
thumbnailSource: nil,
|
|
||||||
contentType: nil),
|
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
|
||||||
ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
|
||||||
timestamp: "10:42",
|
|
||||||
isOutgoing: true,
|
|
||||||
isEditable: true,
|
|
||||||
canBeRepliedTo: true,
|
|
||||||
isThreaded: true,
|
|
||||||
sender: .init(id: ""),
|
|
||||||
content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil),
|
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
|
||||||
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
|
||||||
timestamp: "Now",
|
|
||||||
isOutgoing: false,
|
|
||||||
isEditable: false,
|
|
||||||
canBeRepliedTo: true,
|
|
||||||
isThreaded: true,
|
|
||||||
sender: .init(id: "Bob"),
|
|
||||||
content: .init(body: "Fallback geo uri description",
|
|
||||||
geoURI: .init(latitude: 41.902782,
|
|
||||||
longitude: 12.496366),
|
|
||||||
description: "Location description description description description description description description description"),
|
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
|
||||||
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
|
||||||
timestamp: "Now",
|
|
||||||
isOutgoing: false,
|
|
||||||
isEditable: false,
|
|
||||||
canBeRepliedTo: true,
|
|
||||||
isThreaded: true,
|
|
||||||
sender: .init(id: "Bob"),
|
|
||||||
content: .init(body: "Fallback geo uri description",
|
|
||||||
geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil),
|
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
|
||||||
eventContent: .message(.text(.init(body: "Short"))))))
|
|
||||||
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
|
||||||
timestamp: "10:42",
|
|
||||||
isOutgoing: true,
|
|
||||||
isEditable: false,
|
|
||||||
canBeRepliedTo: true,
|
|
||||||
isThreaded: true,
|
|
||||||
sender: .init(id: ""),
|
|
||||||
content: .init(body: "audio.ogg",
|
|
||||||
duration: 100,
|
|
||||||
waveform: EstimatedWaveform.mockWaveform,
|
|
||||||
source: nil,
|
|
||||||
contentType: nil),
|
|
||||||
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
|
||||||
eventContent: .message(.text(.init(body: "Short"))))),
|
|
||||||
playerState: AudioPlayerState(id: .timelineItemIdentifier(.init(timelineID: "")), duration: 10, waveform: EstimatedWaveform.mockWaveform))
|
|
||||||
}
|
|
||||||
.environmentObject(viewModel.context)
|
|
||||||
}
|
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
VStack(alignment: .leading, spacing: 0) {
|
||||||
ForEach(1..<MockRoomTimelineController().timelineItems.count, id: \.self) { index in
|
ForEach(1..<MockRoomTimelineController().timelineItems.count, id: \.self) { index in
|
||||||
@@ -253,5 +162,142 @@ struct TimelineItemPlainStylerView_Previews: PreviewProvider, TestablePreview {
|
|||||||
.padding()
|
.padding()
|
||||||
.environment(\.timelineStyle, .plain)
|
.environment(\.timelineStyle, .plain)
|
||||||
.previewDisplayName("Threads")
|
.previewDisplayName("Threads")
|
||||||
|
|
||||||
|
replies
|
||||||
|
.environment(\.timelineStyle, .plain)
|
||||||
|
.previewDisplayName("Replies")
|
||||||
|
}
|
||||||
|
|
||||||
|
// These akwats include a reply
|
||||||
|
static var threads: some View {
|
||||||
|
ScrollView {
|
||||||
|
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
|
||||||
|
timestamp: "10:42",
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: true,
|
||||||
|
sender: .init(id: "whoever"),
|
||||||
|
content: .init(body: "A long message that should be on multiple lines."),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))),
|
||||||
|
groupStyle: .single))
|
||||||
|
|
||||||
|
AudioRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
||||||
|
timestamp: "10:42",
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: true,
|
||||||
|
sender: .init(id: ""),
|
||||||
|
content: .init(body: "audio.ogg",
|
||||||
|
duration: 100,
|
||||||
|
waveform: nil,
|
||||||
|
source: nil,
|
||||||
|
contentType: nil),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
|
FileRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
||||||
|
timestamp: "10:42",
|
||||||
|
isOutgoing: false,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: true,
|
||||||
|
sender: .init(id: ""),
|
||||||
|
content: .init(body: "File",
|
||||||
|
source: nil,
|
||||||
|
thumbnailSource: nil,
|
||||||
|
contentType: nil),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
|
ImageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
||||||
|
timestamp: "10:42",
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: true,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: true,
|
||||||
|
sender: .init(id: ""),
|
||||||
|
content: .init(body: "Some image", source: MediaSourceProxy(url: .picturesDirectory, mimeType: "image/png"), thumbnailSource: nil),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
|
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
||||||
|
timestamp: "Now",
|
||||||
|
isOutgoing: false,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: true,
|
||||||
|
sender: .init(id: "Bob"),
|
||||||
|
content: .init(body: "Fallback geo uri description",
|
||||||
|
geoURI: .init(latitude: 41.902782,
|
||||||
|
longitude: 12.496366),
|
||||||
|
description: "Location description description description description description description description description"),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
|
LocationRoomTimelineView(timelineItem: .init(id: .random,
|
||||||
|
timestamp: "Now",
|
||||||
|
isOutgoing: false,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: true,
|
||||||
|
sender: .init(id: "Bob"),
|
||||||
|
content: .init(body: "Fallback geo uri description",
|
||||||
|
geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: nil),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))))
|
||||||
|
VoiceMessageRoomTimelineView(timelineItem: .init(id: .init(timelineID: ""),
|
||||||
|
timestamp: "10:42",
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: true,
|
||||||
|
sender: .init(id: ""),
|
||||||
|
content: .init(body: "audio.ogg",
|
||||||
|
duration: 100,
|
||||||
|
waveform: EstimatedWaveform.mockWaveform,
|
||||||
|
source: nil,
|
||||||
|
contentType: nil),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))),
|
||||||
|
playerState: AudioPlayerState(id: .timelineItemIdentifier(.init(timelineID: "")), duration: 10, waveform: EstimatedWaveform.mockWaveform))
|
||||||
|
}
|
||||||
|
.environmentObject(viewModel.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
static var replies: some View {
|
||||||
|
ScrollView {
|
||||||
|
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
|
||||||
|
timestamp: "10:42",
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: false,
|
||||||
|
sender: .init(id: "whoever"),
|
||||||
|
content: .init(body: "A long message that should be on multiple lines."),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "Short"))))),
|
||||||
|
groupStyle: .single))
|
||||||
|
|
||||||
|
RoomTimelineItemView(viewState: .init(item: TextRoomTimelineItem(id: .init(timelineID: ""),
|
||||||
|
timestamp: "10:42",
|
||||||
|
isOutgoing: true,
|
||||||
|
isEditable: false,
|
||||||
|
canBeRepliedTo: true,
|
||||||
|
isThreaded: false,
|
||||||
|
sender: .init(id: "whoever"),
|
||||||
|
content: .init(body: "Short message"),
|
||||||
|
replyDetails: .loaded(sender: .init(id: "", displayName: "Alice"),
|
||||||
|
eventID: "123",
|
||||||
|
eventContent: .message(.text(.init(body: "A long message that should be on more than 2 lines and so will be clipped by the layout."))))),
|
||||||
|
groupStyle: .single))
|
||||||
|
}
|
||||||
|
.environmentObject(viewModel.context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ struct LocationRoomTimelineView_Previews: PreviewProvider, TestablePreview {
|
|||||||
content: .init(body: "Fallback geo uri description",
|
content: .init(body: "Fallback geo uri description",
|
||||||
geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: "Location description description description description description description description description"),
|
geoURI: .init(latitude: 41.902782, longitude: 12.496366), description: "Location description description description description description description description description"),
|
||||||
replyDetails: .loaded(sender: .init(id: "Someone"),
|
replyDetails: .loaded(sender: .init(id: "Someone"),
|
||||||
|
eventID: "123",
|
||||||
eventContent: .message(.text(.init(body: "The thread content goes 'ere."))))))
|
eventContent: .message(.text(.init(body: "The thread content goes 'ere."))))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -19,8 +19,15 @@ import Foundation
|
|||||||
enum TimelineItemReplyDetails: Hashable {
|
enum TimelineItemReplyDetails: Hashable {
|
||||||
case notLoaded(eventID: String)
|
case notLoaded(eventID: String)
|
||||||
case loading(eventID: String)
|
case loading(eventID: String)
|
||||||
case loaded(sender: TimelineItemSender, eventContent: TimelineEventContent)
|
case loaded(sender: TimelineItemSender, eventID: String, eventContent: TimelineEventContent)
|
||||||
case error(eventID: String, message: String)
|
case error(eventID: String, message: String)
|
||||||
|
|
||||||
|
var eventID: String {
|
||||||
|
switch self {
|
||||||
|
case .notLoaded(let eventID), .loading(let eventID), .loaded(_, let eventID, _), .error(let eventID, _):
|
||||||
|
return eventID
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TimelineEventContent: Hashable {
|
enum TimelineEventContent: Hashable {
|
||||||
|
|||||||
@@ -640,7 +640,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
switch timelineItem.kind() {
|
switch timelineItem.kind() {
|
||||||
case .message:
|
case .message:
|
||||||
return timelineItemReplyDetails(for: timelineItem.asMessage()?.msgtype(), sender: sender)
|
return timelineItemReplyDetails(sender: sender, eventID: details.eventId, messageType: timelineItem.asMessage()?.msgtype())
|
||||||
case .poll(let question, _, _, _, _, _, _):
|
case .poll(let question, _, _, _, _, _, _):
|
||||||
replyContent = .poll(question: question)
|
replyContent = .poll(question: question)
|
||||||
case .sticker(let body, _, _):
|
case .sticker(let body, _, _):
|
||||||
@@ -651,13 +651,13 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
replyContent = .message(.text(.init(body: L10n.commonUnsupportedEvent)))
|
replyContent = .message(.text(.init(body: L10n.commonUnsupportedEvent)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return .loaded(sender: sender, eventContent: replyContent)
|
return .loaded(sender: sender, eventID: details.eventId, eventContent: replyContent)
|
||||||
case let .error(message):
|
case let .error(message):
|
||||||
return .error(eventID: details.eventId, message: message)
|
return .error(eventID: details.eventId, message: message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func timelineItemReplyDetails(for messageType: MessageType?, sender: TimelineItemSender) -> TimelineItemReplyDetails {
|
private func timelineItemReplyDetails(sender: TimelineItemSender, eventID: String, messageType: MessageType?) -> TimelineItemReplyDetails {
|
||||||
let replyContent: EventBasedMessageTimelineItemContentType
|
let replyContent: EventBasedMessageTimelineItemContentType
|
||||||
|
|
||||||
switch messageType {
|
switch messageType {
|
||||||
@@ -685,7 +685,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
replyContent = .text(.init(body: L10n.commonUnsupportedEvent))
|
replyContent = .text(.init(body: L10n.commonUnsupportedEvent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return .loaded(sender: sender, eventContent: .message(replyContent))
|
return .loaded(sender: sender,
|
||||||
|
eventID: eventID,
|
||||||
|
eventContent: .message(replyContent))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user