mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Add permalink detection and custom attributed string attributes (#536)
* Pass full attributed string to TimelineItems and let the UI level handle blockquote coalescing * Add permalink detection and attribute embedding within the attributed string
This commit is contained in:
+1
-1
@@ -34,7 +34,7 @@ enum TimelineItemGroupState: Hashable {
|
||||
}
|
||||
|
||||
protocol EventBasedTimelineItemProtocol: RoomTimelineItemProtocol {
|
||||
var text: String { get }
|
||||
var body: String { get }
|
||||
var timestamp: String { get }
|
||||
var shouldShowSenderDetails: Bool { get }
|
||||
var groupState: TimelineItemGroupState { get }
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@ import UIKit
|
||||
|
||||
struct EmoteRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
var attributedComponents: [AttributedStringBuilderComponent]?
|
||||
let body: String
|
||||
var formattedBody: AttributedString?
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import UIKit
|
||||
|
||||
struct FileRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ import UniformTypeIdentifiers
|
||||
|
||||
struct ImageRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@ import UIKit
|
||||
|
||||
struct NoticeRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
var attributedComponents: [AttributedStringBuilderComponent]?
|
||||
let body: String
|
||||
var formattedBody: AttributedString?
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+2
-2
@@ -18,8 +18,8 @@ import UIKit
|
||||
|
||||
struct TextRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
var attributedComponents: [AttributedStringBuilderComponent]?
|
||||
let body: String
|
||||
var formattedBody: AttributedString?
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import UIKit
|
||||
|
||||
struct VideoRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ struct EncryptedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable,
|
||||
}
|
||||
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
let encryptionType: EncryptionType
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ import UIKit
|
||||
|
||||
struct RedactedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import UIKit
|
||||
|
||||
struct StateRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import UIKit
|
||||
|
||||
struct StickerRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
let timestamp: String
|
||||
let groupState: TimelineItemGroupState
|
||||
let isOutgoing: Bool
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ import UIKit
|
||||
|
||||
struct UnsupportedRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hashable {
|
||||
let id: String
|
||||
let text: String
|
||||
let body: String
|
||||
|
||||
let eventType: String
|
||||
let error: String
|
||||
|
||||
@@ -98,7 +98,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
_ isOutgoing: Bool,
|
||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||
UnsupportedRoomTimelineItem(id: eventItemProxy.id,
|
||||
text: ElementL10n.roomTimelineItemUnsupported,
|
||||
body: ElementL10n.roomTimelineItemUnsupported,
|
||||
eventType: eventType,
|
||||
error: error,
|
||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
@@ -124,7 +124,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
}
|
||||
|
||||
return StickerRoomTimelineItem(id: eventItemProxy.id,
|
||||
text: body,
|
||||
body: body,
|
||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -154,7 +154,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
}
|
||||
|
||||
return EncryptedRoomTimelineItem(id: eventItemProxy.id,
|
||||
text: ElementL10n.roomTimelineUnableToDecrypt,
|
||||
body: ElementL10n.roomTimelineUnableToDecrypt,
|
||||
encryptionType: encryptionType,
|
||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
@@ -168,7 +168,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
_ isOutgoing: Bool,
|
||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||
RedactedRoomTimelineItem(id: eventItemProxy.id,
|
||||
text: ElementL10n.eventRedacted,
|
||||
body: ElementL10n.eventRedacted,
|
||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -180,12 +180,11 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
private func buildFallbackTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
||||
_ isOutgoing: Bool,
|
||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||
let attributedText = attributedStringBuilder.fromPlain(eventItemProxy.body)
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
let formattedBody = attributedStringBuilder.fromPlain(eventItemProxy.body)
|
||||
|
||||
return TextRoomTimelineItem(id: eventItemProxy.id,
|
||||
text: eventItemProxy.body ?? "",
|
||||
attributedComponents: attributedComponents,
|
||||
body: eventItemProxy.body ?? "",
|
||||
formattedBody: formattedBody,
|
||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -199,12 +198,11 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
_ message: MessageTimelineItem<TextMessageContent>,
|
||||
_ isOutgoing: Bool,
|
||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
let formattedBody = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
|
||||
return TextRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
body: message.body,
|
||||
formattedBody: formattedBody,
|
||||
timestamp: message.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -226,7 +224,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
}
|
||||
|
||||
return ImageRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
body: message.body,
|
||||
timestamp: message.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -254,7 +252,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
}
|
||||
|
||||
return VideoRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
body: message.body,
|
||||
timestamp: message.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -277,7 +275,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
_ isOutgoing: Bool,
|
||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||
FileRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
body: message.body,
|
||||
timestamp: message.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -294,12 +292,11 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
_ message: MessageTimelineItem<NoticeMessageContent>,
|
||||
_ isOutgoing: Bool,
|
||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
let formattedBody = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
|
||||
return NoticeRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
body: message.body,
|
||||
formattedBody: formattedBody,
|
||||
timestamp: message.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -316,18 +313,16 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||
let name = eventItemProxy.sender.displayName ?? eventItemProxy.sender.id
|
||||
|
||||
var attributedText: AttributedString?
|
||||
var formattedBody: AttributedString?
|
||||
if let htmlBody = message.htmlBody {
|
||||
attributedText = attributedStringBuilder.fromHTML("* \(name) \(htmlBody)")
|
||||
formattedBody = attributedStringBuilder.fromHTML("* \(name) \(htmlBody)")
|
||||
} else {
|
||||
attributedText = attributedStringBuilder.fromPlain("* \(name) \(message.body)")
|
||||
formattedBody = attributedStringBuilder.fromPlain("* \(name) \(message.body)")
|
||||
}
|
||||
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
|
||||
return EmoteRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
body: message.body,
|
||||
formattedBody: formattedBody,
|
||||
timestamp: message.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: groupState,
|
||||
isOutgoing: isOutgoing,
|
||||
@@ -381,7 +376,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
||||
|
||||
private func buildStateTimelineItem(eventItemProxy: EventTimelineItemProxy, text: String, isOutgoing: Bool) -> RoomTimelineItemProtocol {
|
||||
StateRoomTimelineItem(id: eventItemProxy.id,
|
||||
text: text,
|
||||
body: text,
|
||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||
groupState: .single,
|
||||
isOutgoing: isOutgoing,
|
||||
|
||||
Reference in New Issue
Block a user