mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 10:33:45 +00:00
Initial timeline HTML support.
This commit is contained in:
@@ -14,10 +14,10 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||
|
||||
var timelineItems: [RoomTimelineItemProtocol] = [SeparatorRoomTimelineItem(id: UUID().uuidString, text: "Yesterday"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString, body: "You rock!", timestamp: "10:10 AM", shouldShowSenderDetails: true, senderId: "Alice"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString, body: "You also rule!", timestamp: "10:11 AM", shouldShowSenderDetails: false, senderId: "Alice"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString, text: "You rock!", timestamp: "10:10 AM", shouldShowSenderDetails: true, senderId: "Alice"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString, text: "You also rule!", timestamp: "10:11 AM", shouldShowSenderDetails: false, senderId: "Alice"),
|
||||
SeparatorRoomTimelineItem(id: UUID().uuidString, text: "Today"),
|
||||
TextRoomTimelineItem(id: UUID().uuidString, body: "You too!", timestamp: "5 PM", shouldShowSenderDetails: true, senderId: "Bob")]
|
||||
TextRoomTimelineItem(id: UUID().uuidString, text: "You too!", timestamp: "5 PM", shouldShowSenderDetails: true, senderId: "Bob")]
|
||||
|
||||
func paginateBackwards(_ count: UInt, callback: ((Result<Void, RoomTimelineControllerError>) -> Void)) {
|
||||
callbacks.send(.updatedTimelineItems)
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
protocol EventBasedTimelineItemProtocol: RoomTimelineItemProtocol {
|
||||
var body: String { get }
|
||||
var text: String { get }
|
||||
var timestamp: String { get }
|
||||
var shouldShowSenderDetails: Bool { get }
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import UIKit
|
||||
|
||||
struct ImageRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equatable {
|
||||
let id: String
|
||||
let body: String
|
||||
let text: String
|
||||
let timestamp: String
|
||||
let shouldShowSenderDetails: Bool
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import UIKit
|
||||
|
||||
struct TextRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equatable {
|
||||
let id: String
|
||||
let body: String
|
||||
var htmlBody: String?
|
||||
let text: String
|
||||
var attributedComponents: [AttributedStringBuilderComponent]?
|
||||
let timestamp: String
|
||||
let shouldShowSenderDetails: Bool
|
||||
|
||||
|
||||
@@ -12,11 +12,14 @@ import UIKit
|
||||
struct RoomTimelineItemFactory {
|
||||
private let mediaProvider: MediaProviderProtocol
|
||||
private let memberDetailsProvider: MemberDetailsProviderProtocol
|
||||
private let attributedStringBuilder: AttributedStringBuilderProtocol
|
||||
|
||||
init(mediaProvider: MediaProviderProtocol,
|
||||
memberDetailsProvider: MemberDetailsProviderProtocol) {
|
||||
memberDetailsProvider: MemberDetailsProviderProtocol,
|
||||
attributedStringBuilder: AttributedStringBuilderProtocol) {
|
||||
self.mediaProvider = mediaProvider
|
||||
self.memberDetailsProvider = memberDetailsProvider
|
||||
self.attributedStringBuilder = attributedStringBuilder
|
||||
}
|
||||
|
||||
func buildTimelineItemFor(_ roomMessage: RoomMessageProtocol, showSenderDetails: Bool) -> RoomTimelineItemProtocol {
|
||||
@@ -27,9 +30,12 @@ struct RoomTimelineItemFactory {
|
||||
|
||||
switch roomMessage {
|
||||
case let message as TextRoomMessage:
|
||||
let attributedText = attributedStringBuilder.fromHTML(message.htmlBody)
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
|
||||
return TextRoomTimelineItem(id: message.id,
|
||||
body: message.body,
|
||||
htmlBody: message.htmlBody,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
@@ -37,7 +43,7 @@ struct RoomTimelineItemFactory {
|
||||
senderAvatar: avatarImage)
|
||||
case let message as ImageRoomMessage:
|
||||
return ImageRoomTimelineItem(id: message.id,
|
||||
body: message.body,
|
||||
text: message.body,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// FormattedBodyText.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 24/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct FormattedBodyText: View {
|
||||
let attributedComponents: [AttributedStringBuilderComponent]
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 0.0) {
|
||||
ForEach(attributedComponents, id: \.self) { component in
|
||||
if component.isBlockquote {
|
||||
HStack(spacing: 4.0) {
|
||||
Rectangle()
|
||||
.foregroundColor(Color.red)
|
||||
.frame(width: 4.0)
|
||||
Text(component.attributedString)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
} else {
|
||||
Text(component.attributedString)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ struct ImageRoomTimelineView: View {
|
||||
if let image = timelineItem.image {
|
||||
VStack(alignment: .leading) {
|
||||
EventBasedTimelineView(timelineItem: timelineItem)
|
||||
Text(timelineItem.body)
|
||||
Text(timelineItem.text)
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
@@ -24,7 +24,7 @@ struct ImageRoomTimelineView: View {
|
||||
} else {
|
||||
VStack(alignment: .center) {
|
||||
HStack {
|
||||
Text(timelineItem.body)
|
||||
Text(timelineItem.text)
|
||||
Spacer()
|
||||
}
|
||||
ProgressView("Loading")
|
||||
@@ -37,7 +37,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
let timelineItem = ImageRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some image",
|
||||
text: "Some image",
|
||||
timestamp: "Now",
|
||||
shouldShowSenderDetails: false,
|
||||
senderId: "Bob",
|
||||
@@ -46,7 +46,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
||||
ImageRoomTimelineView(timelineItem: timelineItem)
|
||||
|
||||
let timelineItem = ImageRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some other image",
|
||||
text: "Some other image",
|
||||
timestamp: "Now",
|
||||
shouldShowSenderDetails: false,
|
||||
senderId: "Bob",
|
||||
|
||||
@@ -15,52 +15,28 @@ struct TextRoomTimelineView: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
EventBasedTimelineView(timelineItem: timelineItem)
|
||||
|
||||
if let htmlString = buildHtmlString() {
|
||||
Text(AttributedString(htmlString))
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
if let components = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: components)
|
||||
} else {
|
||||
if let attributedString = try? AttributedString(markdown: timelineItem.body) {
|
||||
Text(attributedString)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
} else {
|
||||
Text(timelineItem.body)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
}
|
||||
Text(timelineItem.text)
|
||||
}
|
||||
}
|
||||
.id(timelineItem.id)
|
||||
}
|
||||
|
||||
private func buildHtmlString() -> NSAttributedString? {
|
||||
guard let formattedText = timelineItem.htmlBody,
|
||||
let encodedData = formattedText.data(using: String.Encoding.utf8) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
do {
|
||||
return try NSAttributedString(data: encodedData, options: [
|
||||
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
|
||||
NSAttributedString.DocumentReadingOptionKey.characterEncoding: NSNumber(value: String.Encoding.utf8.rawValue)
|
||||
], documentAttributes: nil)
|
||||
} catch {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct TextRoomTimelineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack(spacing: 20.0) {
|
||||
let timelineItem = TextRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Short loin ground round tongue hamburger, fatback salami shoulder. Beef turkey sausage kielbasa strip steak. Alcatra capicola pig tail pancetta chislic.",
|
||||
text: "Short loin ground round tongue hamburger, fatback salami shoulder. Beef turkey sausage kielbasa strip steak. Alcatra capicola pig tail pancetta chislic.",
|
||||
timestamp: "Now",
|
||||
shouldShowSenderDetails: true,
|
||||
senderId: "Bob")
|
||||
TextRoomTimelineView(timelineItem: timelineItem)
|
||||
|
||||
let timelineItem = TextRoomTimelineItem(id: UUID().uuidString,
|
||||
body: "Some other text",
|
||||
text: "Some other text",
|
||||
timestamp: "Later",
|
||||
shouldShowSenderDetails: true,
|
||||
senderId: "Anne")
|
||||
|
||||
Reference in New Issue
Block a user