Adopting different timeline item types exposed from rust.

This commit is contained in:
Stefan Ceriu
2022-03-16 13:30:55 +02:00
parent 44da96c55f
commit 3cc1395fbc
14 changed files with 219 additions and 73 deletions
@@ -0,0 +1,34 @@
//
// ImageRoomMessage.swift
// ElementX
//
// Created by Stefan Ceriu on 16/03/2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import MatrixRustSDK
struct ImageRoomMessage: RoomMessageProtocol {
private let message: MatrixRustSDK.ImageMessage
init(message: MatrixRustSDK.ImageMessage) {
self.message = message
}
var id: String {
message.baseMessage().id()
}
var content: String {
message.baseMessage().content()
}
var sender: String {
message.baseMessage().sender()
}
var originServerTs: Date {
Date(timeIntervalSince1970: TimeInterval(message.baseMessage().originServerTs()))
}
}
@@ -0,0 +1,22 @@
//
// RoomMessageFactory.swift
// ElementX
//
// Created by Stefan Ceriu on 16/03/2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import MatrixRustSDK
struct RoomMessageFactory {
func buildRoomMessageFrom(_ message: AnyMessage) -> RoomMessageProtocol {
if let textMessage = message.text() {
return TextRoomMessage(message: textMessage)
} else if let imageMessage = message.image() {
return ImageRoomMessage(message: imageMessage)
} else {
fatalError("One of these must exist")
}
}
}
@@ -0,0 +1,16 @@
//
// RoomMessageProtocol.swift
// ElementX
//
// Created by Stefan Ceriu on 16/03/2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
protocol RoomMessageProtocol {
var id: String { get }
var content: String { get }
var sender: String { get }
var originServerTs: Date { get }
}
@@ -0,0 +1,35 @@
//
// TextRoomMessage.swift
// ElementX
//
// Created by Stefan Ceriu on 16/03/2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import UIKit
import MatrixRustSDK
struct TextRoomMessage: RoomMessageProtocol {
private let message: MatrixRustSDK.TextMessage
init(message: MatrixRustSDK.TextMessage) {
self.message = message
}
var id: String {
message.baseMessage().id()
}
var content: String {
message.baseMessage().content()
}
var sender: String {
message.baseMessage().sender()
}
var originServerTs: Date {
Date(timeIntervalSince1970: TimeInterval(message.baseMessage().originServerTs()))
}
}
@@ -0,0 +1,45 @@
//
// MockRoomProxy.swift
// ElementX
//
// Created by Stefan Ceriu on 17.02.2022.
//
import Foundation
import UIKit
import Combine
import MatrixRustSDK
struct MockRoomProxy: RoomProxyProtocol {
let id = UUID().uuidString
let name: String? = nil
let displayName: String
let topic: String? = nil
let lastMessage: String? = "Last message"
let avatarURL: URL? = nil
let isDirect = Bool.random()
let isSpace = Bool.random()
let isPublic = Bool.random()
let isEncrypted = Bool.random()
var callbacks = PassthroughSubject<RoomProxyCallback, Never>()
func loadDisplayName(_ completion: @escaping (Result<String, Error>) -> Void) {
completion(.success(displayName))
}
func loadAvatar(_ completion: (Result<UIImage?, Error>) -> Void) {
completion(.success(UIImage(systemName: "wand.and.stars")))
}
func startLiveEventListener() {
}
func paginateBackwards(count: UInt, callback: ((Result<[RoomMessageProtocol], Error>) -> Void)?) {
}
}
@@ -0,0 +1,174 @@
//
// RoomProxy.swift
// ElementX
//
// Created by Stefan Ceriu on 14.02.2022.
//
import Foundation
import UIKit
import Combine
import MatrixRustSDK
enum RoomProxyError: Error {
case failedRetrievingDisplayName
case failedRetrievingAvatar
case backwardStreamNotAvailable
}
private class WeakRoomProxyWrapper: RoomDelegate {
private weak var roomProxy: RoomProxy?
init(roomProxy: RoomProxy) {
self.roomProxy = roomProxy
}
// MARK: - RoomDelegate
func didReceiveMessage(message: AnyMessage) {
DispatchQueue.main.async {
self.roomProxy?.appendMessage(message)
}
}
}
class RoomProxy: RoomProxyProtocol, Equatable {
private let room: Room
private let messageFactory: RoomMessageFactory
private let processingQueue: DispatchQueue
private var backwardStream: BackwardsStreamProtocol?
let callbacks = PassthroughSubject<RoomProxyCallback, Never>()
init(room: Room, messageFactory: RoomMessageFactory) {
self.room = room
self.messageFactory = messageFactory
processingQueue = DispatchQueue(label: "RoomProxyProcessingQueue")
processingQueue.async {
self.backwardStream = room.startLiveEventListener()
}
room.setDelegate(delegate: WeakRoomProxyWrapper(roomProxy: self))
}
var id: String {
return room.id()
}
var isDirect: Bool {
return room.isDirect()
}
var isPublic: Bool {
return room.isPublic()
}
var isSpace: Bool {
return room.isSpace()
}
var isEncrypted: Bool {
return room.isEncrypted()
}
var name: String? {
return room.name()
}
var topic: String? {
return room.topic()
}
var lastMessage: String? {
didSet {
if lastMessage == oldValue {
return
}
callbacks.send(.updatedLastMessage)
}
}
var avatarURL: URL? {
guard let urlString = room.avatarUrl() else {
return nil
}
return URL(string: urlString)
}
func loadDisplayName(_ completion: @escaping (Result<String, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let displayName = try self.room.displayName()
DispatchQueue.main.async {
completion(.success(displayName))
}
} catch {
DispatchQueue.main.async {
completion(.failure(RoomProxyError.failedRetrievingDisplayName))
}
}
}
}
func loadAvatar(_ completion: @escaping (Result<UIImage?, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let avatarData = try self.room.avatar()
DispatchQueue.main.async {
completion(.success(UIImage(data: Data(bytes: avatarData, count: avatarData.count))))
}
} catch {
DispatchQueue.main.async {
completion(.failure(RoomProxyError.failedRetrievingAvatar))
}
}
}
}
func paginateBackwards(count: UInt, callback: ((Result<[RoomMessageProtocol], Error>) -> Void)?) {
MXLog.debug("Started backpaginating")
processingQueue.async {
guard let backwardStream = self.backwardStream else {
DispatchQueue.main.async {
callback?(.failure(RoomProxyError.backwardStreamNotAvailable))
}
return
}
let messages = backwardStream.paginateBackwards(count: UInt64(count)).map { message in
self.messageFactory.buildRoomMessageFrom(message)
}
MXLog.debug("Finished backpaginating")
DispatchQueue.main.async {
callback?(.success(messages))
if self.lastMessage == nil {
self.lastMessage = messages.last?.content ?? ""
}
}
}
}
// MARK: - Equatable
static func == (lhs: RoomProxy, rhs: RoomProxy) -> Bool {
lhs.id == rhs.id
}
// MARK: - Private
fileprivate func appendMessage(_ message: AnyMessage) {
let message = self.messageFactory.buildRoomMessageFrom(message)
lastMessage = message.content
callbacks.send(.addedMessage(message))
}
}
@@ -0,0 +1,37 @@
//
// RoomProxyProtocol.swift
// ElementX
//
// Created by Stefan Ceriu on 17.02.2022.
//
import UIKit
import Combine
import MatrixRustSDK
enum RoomProxyCallback {
case addedMessage(RoomMessageProtocol)
case updatedLastMessage
}
protocol RoomProxyProtocol {
var id: String { get }
var isDirect: Bool { get }
var isPublic: Bool { get }
var isSpace: Bool { get }
var isEncrypted: Bool { get }
var name: String? { get }
var topic: String? { get }
var lastMessage: String? { get }
var avatarURL: URL? { get }
func loadDisplayName(_ completion: @escaping (Result<String, Error>) -> Void)
func loadAvatar(_ completion: @escaping (Result<UIImage?, Error>) -> Void)
func paginateBackwards(count: UInt, callback: ((Result<[RoomMessageProtocol], Error>) -> Void)?)
var callbacks: PassthroughSubject<RoomProxyCallback, Never> { get }
}