mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-02 21:33:26 +00:00
Added support for emotes and notices.
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// EmoteRoomMessage.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 16/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import MatrixRustSDK
|
||||
|
||||
struct EmoteRoomMessage: RoomMessageProtocol {
|
||||
private let message: MatrixRustSDK.EmoteMessage
|
||||
|
||||
init(message: MatrixRustSDK.EmoteMessage) {
|
||||
self.message = message
|
||||
}
|
||||
|
||||
var id: String {
|
||||
message.baseMessage().id()
|
||||
}
|
||||
|
||||
var body: String {
|
||||
message.baseMessage().body()
|
||||
}
|
||||
|
||||
var htmlBody: String? {
|
||||
message.htmlBody()
|
||||
}
|
||||
|
||||
var sender: String {
|
||||
message.baseMessage().sender()
|
||||
}
|
||||
|
||||
var originServerTs: Date {
|
||||
Date(timeIntervalSince1970: TimeInterval(message.baseMessage().originServerTs()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// NoticeRoomMessage.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 16/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import MatrixRustSDK
|
||||
|
||||
struct NoticeRoomMessage: RoomMessageProtocol {
|
||||
private let message: MatrixRustSDK.NoticeMessage
|
||||
|
||||
init(message: MatrixRustSDK.NoticeMessage) {
|
||||
self.message = message
|
||||
}
|
||||
|
||||
var id: String {
|
||||
message.baseMessage().id()
|
||||
}
|
||||
|
||||
var body: String {
|
||||
message.baseMessage().body()
|
||||
}
|
||||
|
||||
var htmlBody: String? {
|
||||
message.htmlBody()
|
||||
}
|
||||
|
||||
var sender: String {
|
||||
message.baseMessage().sender()
|
||||
}
|
||||
|
||||
var originServerTs: Date {
|
||||
Date(timeIntervalSince1970: TimeInterval(message.baseMessage().originServerTs()))
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,14 @@ import MatrixRustSDK
|
||||
|
||||
struct RoomMessageFactory {
|
||||
func buildRoomMessageFrom(_ message: AnyMessage) -> RoomMessageProtocol {
|
||||
if let textMessage = message.text() {
|
||||
if let textMessage = message.textMessage() {
|
||||
return TextRoomMessage(message: textMessage)
|
||||
} else if let imageMessage = message.image() {
|
||||
} else if let imageMessage = message.imageMessage() {
|
||||
return ImageRoomMessage(message: imageMessage)
|
||||
} else if let noticeMessage = message.noticeMessage() {
|
||||
return NoticeRoomMessage(message: noticeMessage)
|
||||
} else if let emoteMessage = message.emoteMessage() {
|
||||
return EmoteRoomMessage(message: emoteMessage)
|
||||
} else {
|
||||
fatalError("One of these must exist")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// EmoteRoomTimelineItem.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 11/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
struct EmoteRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equatable {
|
||||
let id: String
|
||||
let text: String
|
||||
var attributedComponents: [AttributedStringBuilderComponent]?
|
||||
let timestamp: String
|
||||
let shouldShowSenderDetails: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
var senderAvatar: UIImage?
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// NoticeRoomTimelineItem.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 11/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
struct NoticeRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equatable {
|
||||
let id: String
|
||||
let text: String
|
||||
var attributedComponents: [AttributedStringBuilderComponent]?
|
||||
let timestamp: String
|
||||
let shouldShowSenderDetails: Bool
|
||||
|
||||
let senderId: String
|
||||
var senderDisplayName: String?
|
||||
var senderAvatar: UIImage?
|
||||
}
|
||||
@@ -23,37 +23,88 @@ struct RoomTimelineItemFactory {
|
||||
}
|
||||
|
||||
func buildTimelineItemFor(_ roomMessage: RoomMessageProtocol, showSenderDetails: Bool) -> RoomTimelineItemProtocol {
|
||||
|
||||
let displayName = memberDetailsProvider.displayNameForUserId(roomMessage.sender)
|
||||
let avatarURL = memberDetailsProvider.avatarURLForUserId(roomMessage.sender)
|
||||
let avatarImage = mediaProvider.imageForURL(avatarURL)
|
||||
|
||||
switch roomMessage {
|
||||
case let message as TextRoomMessage:
|
||||
|
||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
|
||||
return TextRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage)
|
||||
return buildTextTimelineItemFromMessage(message, showSenderDetails, displayName, avatarImage)
|
||||
case let message as ImageRoomMessage:
|
||||
return ImageRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
url: message.url,
|
||||
image: mediaProvider.imageForURL(message.url))
|
||||
return buildImageTimelineItemFromMessage(message, showSenderDetails, displayName, avatarImage)
|
||||
case let message as NoticeRoomMessage:
|
||||
return buildNoticeTimelineItemFromMessage(message, showSenderDetails, displayName, avatarImage)
|
||||
case let message as EmoteRoomMessage:
|
||||
return buildEmoteTimelineItemFromMessage(message, showSenderDetails, displayName, avatarImage)
|
||||
default:
|
||||
fatalError("Unknown room message.")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
private func buildTextTimelineItemFromMessage(_ message: TextRoomMessage,
|
||||
_ showSenderDetails: Bool,
|
||||
_ displayName: String?,
|
||||
_ avatarImage: UIImage?) -> RoomTimelineItemProtocol {
|
||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
|
||||
return TextRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage)
|
||||
}
|
||||
|
||||
private func buildImageTimelineItemFromMessage(_ message: ImageRoomMessage,
|
||||
_ showSenderDetails: Bool,
|
||||
_ displayName: String?,
|
||||
_ avatarImage: UIImage?) -> RoomTimelineItemProtocol {
|
||||
return ImageRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage,
|
||||
url: message.url,
|
||||
image: mediaProvider.imageForURL(message.url))
|
||||
}
|
||||
|
||||
private func buildNoticeTimelineItemFromMessage(_ message: NoticeRoomMessage,
|
||||
_ showSenderDetails: Bool,
|
||||
_ displayName: String?,
|
||||
_ avatarImage: UIImage?) -> RoomTimelineItemProtocol {
|
||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
|
||||
return NoticeRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage)
|
||||
}
|
||||
|
||||
private func buildEmoteTimelineItemFromMessage(_ message: EmoteRoomMessage,
|
||||
_ showSenderDetails: Bool,
|
||||
_ displayName: String?,
|
||||
_ avatarImage: UIImage?) -> RoomTimelineItemProtocol {
|
||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||
let attributedComponents = attributedStringBuilder.blockquoteCoalescedComponentsFrom(attributedText)
|
||||
|
||||
return EmoteRoomTimelineItem(id: message.id,
|
||||
text: message.body,
|
||||
attributedComponents: attributedComponents,
|
||||
timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened),
|
||||
shouldShowSenderDetails: showSenderDetails,
|
||||
senderId: message.sender,
|
||||
senderDisplayName: displayName,
|
||||
senderAvatar: avatarImage)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ struct RoomTimelineViewFactory {
|
||||
return .image(item)
|
||||
case let item as SeparatorRoomTimelineItem:
|
||||
return .separator(item)
|
||||
case let item as NoticeRoomTimelineItem:
|
||||
return .notice(item)
|
||||
case let item as EmoteRoomTimelineItem:
|
||||
return .emote(item)
|
||||
default:
|
||||
fatalError("Unknown timeline item")
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ enum RoomTimelineViewProvider: Identifiable, Equatable {
|
||||
case text(TextRoomTimelineItem)
|
||||
case separator(SeparatorRoomTimelineItem)
|
||||
case image(ImageRoomTimelineItem)
|
||||
case emote(EmoteRoomTimelineItem)
|
||||
case notice(NoticeRoomTimelineItem)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
@@ -22,6 +24,10 @@ enum RoomTimelineViewProvider: Identifiable, Equatable {
|
||||
return item.id
|
||||
case .image(let item):
|
||||
return item.id
|
||||
case .emote(let item):
|
||||
return item.id
|
||||
case .notice(let item):
|
||||
return item.id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +41,10 @@ extension RoomTimelineViewProvider: View {
|
||||
SeparatorRoomTimelineView(timelineItem: item)
|
||||
case .image(let item):
|
||||
ImageRoomTimelineView(timelineItem: item)
|
||||
case .emote(let item):
|
||||
EmoteRoomTimelineView(timelineItem: item)
|
||||
case .notice(let item):
|
||||
NoticeRoomTimelineView(timelineItem: item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// EmoteRoomTimelineView.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 11/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct EmoteRoomTimelineView: View {
|
||||
let timelineItem: EmoteRoomTimelineItem
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
EventBasedTimelineView(timelineItem: timelineItem)
|
||||
HStack(alignment: .top) {
|
||||
Image(systemName: "face.dashed").padding(.top, 1.0)
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
.id(timelineItem.id)
|
||||
}
|
||||
}
|
||||
|
||||
struct EmoteRoomTimelineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack(spacing: 20.0) {
|
||||
let timelineItem = EmoteRoomTimelineItem(id: UUID().uuidString,
|
||||
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")
|
||||
EmoteRoomTimelineView(timelineItem: timelineItem)
|
||||
|
||||
let timelineItem = EmoteRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "Some other text",
|
||||
timestamp: "Later",
|
||||
shouldShowSenderDetails: true,
|
||||
senderId: "Anne")
|
||||
EmoteRoomTimelineView(timelineItem: timelineItem)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// NoticeRoomTimelineView.swift
|
||||
// ElementX
|
||||
//
|
||||
// Created by Stefan Ceriu on 11/03/2022.
|
||||
// Copyright © 2022 Element. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
struct NoticeRoomTimelineView: View {
|
||||
let timelineItem: NoticeRoomTimelineItem
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
EventBasedTimelineView(timelineItem: timelineItem)
|
||||
HStack(alignment: .top) {
|
||||
Image(systemName: "exclamationmark.bubble").padding(.top, 2.0)
|
||||
if let attributedComponents = timelineItem.attributedComponents {
|
||||
FormattedBodyText(attributedComponents: attributedComponents)
|
||||
} else {
|
||||
Text(timelineItem.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
.id(timelineItem.id)
|
||||
}
|
||||
}
|
||||
|
||||
struct NoticeRoomTimelineView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
VStack(spacing: 20.0) {
|
||||
let timelineItem = NoticeRoomTimelineItem(id: UUID().uuidString,
|
||||
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")
|
||||
NoticeRoomTimelineView(timelineItem: timelineItem)
|
||||
|
||||
let timelineItem = NoticeRoomTimelineItem(id: UUID().uuidString,
|
||||
text: "Some other text",
|
||||
timestamp: "Later",
|
||||
shouldShowSenderDetails: true,
|
||||
senderId: "Anne")
|
||||
NoticeRoomTimelineView(timelineItem: timelineItem)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user