mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 19:44:05 +00:00
Caching memberDetailProviders, showing sender display names in home screen last message, cleaner homescreen design.
This commit is contained in:
@@ -26,6 +26,7 @@
|
|||||||
1850257127B6A135002E6B18 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1850256927B6A135002E6B18 /* LaunchScreen.storyboard */; };
|
1850257127B6A135002E6B18 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1850256927B6A135002E6B18 /* LaunchScreen.storyboard */; };
|
||||||
1863A3FC27BA5A9100B52E4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A3FB27BA5A9100B52E4D /* KeychainAccess */; };
|
1863A3FC27BA5A9100B52E4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A3FB27BA5A9100B52E4D /* KeychainAccess */; };
|
||||||
1863A40627BA6DFC00B52E4D /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A40527BA6DFC00B52E4D /* SwiftyBeaver */; };
|
1863A40627BA6DFC00B52E4D /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A40527BA6DFC00B52E4D /* SwiftyBeaver */; };
|
||||||
|
186AD17D27F72E5200048C8E /* MemberDetailProviderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 186AD17C27F72E5200048C8E /* MemberDetailProviderManager.swift */; };
|
||||||
18920C0E27F233FF00A717B5 /* NoticeRoomMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C0C27F233FF00A717B5 /* NoticeRoomMessage.swift */; };
|
18920C0E27F233FF00A717B5 /* NoticeRoomMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C0C27F233FF00A717B5 /* NoticeRoomMessage.swift */; };
|
||||||
18920C0F27F233FF00A717B5 /* EmoteRoomMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C0D27F233FF00A717B5 /* EmoteRoomMessage.swift */; };
|
18920C0F27F233FF00A717B5 /* EmoteRoomMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C0D27F233FF00A717B5 /* EmoteRoomMessage.swift */; };
|
||||||
18920C1227F2347600A717B5 /* NoticeRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1027F2347600A717B5 /* NoticeRoomTimelineItem.swift */; };
|
18920C1227F2347600A717B5 /* NoticeRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18920C1027F2347600A717B5 /* NoticeRoomTimelineItem.swift */; };
|
||||||
@@ -166,6 +167,7 @@
|
|||||||
1850256727B6A135002E6B18 /* ElementX.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = ElementX.entitlements; sourceTree = "<group>"; };
|
1850256727B6A135002E6B18 /* ElementX.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = ElementX.entitlements; sourceTree = "<group>"; };
|
||||||
1850256827B6A135002E6B18 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
1850256827B6A135002E6B18 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
1850256A27B6A135002E6B18 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
1850256A27B6A135002E6B18 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
|
186AD17C27F72E5200048C8E /* MemberDetailProviderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDetailProviderManager.swift; sourceTree = "<group>"; };
|
||||||
18920C0C27F233FF00A717B5 /* NoticeRoomMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoticeRoomMessage.swift; sourceTree = "<group>"; };
|
18920C0C27F233FF00A717B5 /* NoticeRoomMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoticeRoomMessage.swift; sourceTree = "<group>"; };
|
||||||
18920C0D27F233FF00A717B5 /* EmoteRoomMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmoteRoomMessage.swift; sourceTree = "<group>"; };
|
18920C0D27F233FF00A717B5 /* EmoteRoomMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmoteRoomMessage.swift; sourceTree = "<group>"; };
|
||||||
18920C1027F2347600A717B5 /* NoticeRoomTimelineItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItem.swift; sourceTree = "<group>"; };
|
18920C1027F2347600A717B5 /* NoticeRoomTimelineItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
@@ -487,6 +489,7 @@
|
|||||||
children = (
|
children = (
|
||||||
183E023327E4A73C00903BED /* MemberDetailsProviderProtocol.swift */,
|
183E023327E4A73C00903BED /* MemberDetailsProviderProtocol.swift */,
|
||||||
18DF7C5227E4754500291672 /* MemberDetailsProvider.swift */,
|
18DF7C5227E4754500291672 /* MemberDetailsProvider.swift */,
|
||||||
|
186AD17C27F72E5200048C8E /* MemberDetailProviderManager.swift */,
|
||||||
);
|
);
|
||||||
path = Members;
|
path = Members;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -955,6 +958,7 @@
|
|||||||
189E496527F4777400D86BA3 /* NoticeRoomTimelineView.swift in Sources */,
|
189E496527F4777400D86BA3 /* NoticeRoomTimelineView.swift in Sources */,
|
||||||
18F2BAED27D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift in Sources */,
|
18F2BAED27D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift in Sources */,
|
||||||
18DF7C4127E4670600291672 /* RoomTimelineViewProvider.swift in Sources */,
|
18DF7C4127E4670600291672 /* RoomTimelineViewProvider.swift in Sources */,
|
||||||
|
186AD17D27F72E5200048C8E /* MemberDetailProviderManager.swift in Sources */,
|
||||||
18F2BB0F27D25B4000DD1988 /* RoomScreen.swift in Sources */,
|
18F2BB0F27D25B4000DD1988 /* RoomScreen.swift in Sources */,
|
||||||
18F2BAFF27D25B4000DD1988 /* HomeScreenModels.swift in Sources */,
|
18F2BAFF27D25B4000DD1988 /* HomeScreenModels.swift in Sources */,
|
||||||
183E023427E4A73C00903BED /* MemberDetailsProviderProtocol.swift in Sources */,
|
183E023427E4A73C00903BED /* MemberDetailsProviderProtocol.swift in Sources */,
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
|||||||
private let keychainController: KeychainControllerProtocol
|
private let keychainController: KeychainControllerProtocol
|
||||||
private let authenticationCoordinator: AuthenticationCoordinator!
|
private let authenticationCoordinator: AuthenticationCoordinator!
|
||||||
|
|
||||||
|
private let memberDetailProviderManager: MemberDetailProviderManager
|
||||||
|
|
||||||
private var loadingActivity: Activity?
|
private var loadingActivity: Activity?
|
||||||
private var errorActivity: Activity?
|
private var errorActivity: Activity?
|
||||||
|
|
||||||
@@ -31,6 +33,8 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
|||||||
|
|
||||||
navigationRouter = NavigationRouter(navigationController: mainNavigationController)
|
navigationRouter = NavigationRouter(navigationController: mainNavigationController)
|
||||||
|
|
||||||
|
memberDetailProviderManager = MemberDetailProviderManager()
|
||||||
|
|
||||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier else {
|
guard let bundleIdentifier = Bundle.main.bundleIdentifier else {
|
||||||
fatalError("Should have a valid bundle identifier at this point")
|
fatalError("Should have a valid bundle identifier at this point")
|
||||||
}
|
}
|
||||||
@@ -82,8 +86,8 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
|||||||
|
|
||||||
let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
|
let parameters = HomeScreenCoordinatorParameters(userSession: userSession,
|
||||||
mediaProvider: userSession.mediaProvider,
|
mediaProvider: userSession.mediaProvider,
|
||||||
eventBriefFactory: EventBriefFactory(),
|
attributedStringBuilder: AttributedStringBuilder(),
|
||||||
attributedStringBuilder: AttributedStringBuilder())
|
memberDetailProviderManager: memberDetailProviderManager)
|
||||||
let coordinator = HomeScreenCoordinator(parameters: parameters)
|
let coordinator = HomeScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
coordinator.completion = { [weak self] result in
|
coordinator.completion = { [weak self] result in
|
||||||
@@ -109,16 +113,16 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let memberDetailsProvider = MemberDetailsProvider(roomProxy: roomProxy)
|
let memberDetailProvider = memberDetailProviderManager.memberDetailProviderForRoomProxy(roomProxy)
|
||||||
|
|
||||||
let timelineItemFactory = RoomTimelineItemFactory(mediaProvider: userSession.mediaProvider,
|
let timelineItemFactory = RoomTimelineItemFactory(mediaProvider: userSession.mediaProvider,
|
||||||
memberDetailsProvider: memberDetailsProvider,
|
memberDetailProvider: memberDetailProvider,
|
||||||
attributedStringBuilder: AttributedStringBuilder())
|
attributedStringBuilder: AttributedStringBuilder())
|
||||||
|
|
||||||
let timelineController = RoomTimelineController(timelineProvider: RoomTimelineProvider(roomProxy: roomProxy),
|
let timelineController = RoomTimelineController(timelineProvider: RoomTimelineProvider(roomProxy: roomProxy),
|
||||||
timelineItemFactory: timelineItemFactory,
|
timelineItemFactory: timelineItemFactory,
|
||||||
mediaProvider: userSession.mediaProvider,
|
mediaProvider: userSession.mediaProvider,
|
||||||
memberDetailsProvider: memberDetailsProvider)
|
memberDetailProvider: memberDetailProvider)
|
||||||
|
|
||||||
let parameters = RoomScreenCoordinatorParameters(timelineController: timelineController,
|
let parameters = RoomScreenCoordinatorParameters(timelineController: timelineController,
|
||||||
roomName: roomProxy.name)
|
roomName: roomProxy.name)
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import Combine
|
|||||||
struct HomeScreenCoordinatorParameters {
|
struct HomeScreenCoordinatorParameters {
|
||||||
let userSession: UserSession
|
let userSession: UserSession
|
||||||
let mediaProvider: MediaProviderProtocol
|
let mediaProvider: MediaProviderProtocol
|
||||||
let eventBriefFactory: EventBriefFactoryProtocol
|
|
||||||
let attributedStringBuilder: AttributedStringBuilderProtocol
|
let attributedStringBuilder: AttributedStringBuilderProtocol
|
||||||
|
let memberDetailProviderManager: MemberDetailProviderManager
|
||||||
}
|
}
|
||||||
|
|
||||||
enum HomeScreenCoordinatorResult {
|
enum HomeScreenCoordinatorResult {
|
||||||
@@ -96,14 +96,20 @@ final class HomeScreenCoordinator: Coordinator, Presentable {
|
|||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
func updateRoomsList() {
|
func updateRoomsList() {
|
||||||
self.roomSummaries = parameters.userSession.rooms.map { roomProxy in
|
self.roomSummaries = parameters.userSession.rooms.compactMap { roomProxy in
|
||||||
|
guard !roomProxy.isSpace, !roomProxy.isTombstoned else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if let summary = self.roomSummaries.first(where: { $0.id == roomProxy.id }) {
|
if let summary = self.roomSummaries.first(where: { $0.id == roomProxy.id }) {
|
||||||
return summary
|
return summary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let memberDetailProvider = parameters.memberDetailProviderManager.memberDetailProviderForRoomProxy(roomProxy)
|
||||||
|
|
||||||
return RoomSummary(roomProxy: roomProxy,
|
return RoomSummary(roomProxy: roomProxy,
|
||||||
mediaProvider: parameters.mediaProvider,
|
mediaProvider: parameters.mediaProvider,
|
||||||
eventBriefFactory: parameters.eventBriefFactory)
|
eventBriefFactory: EventBriefFactory(memberDetailProvider: memberDetailProvider))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.viewModel.updateWithRoomList(roomSummaries)
|
self.viewModel.updateWithRoomList(roomSummaries)
|
||||||
|
|||||||
@@ -36,27 +36,19 @@ struct HomeScreenViewState: BindableState {
|
|||||||
var isLoadingRooms: Bool = false
|
var isLoadingRooms: Bool = false
|
||||||
|
|
||||||
var unencryptedDMs: [HomeScreenRoom] {
|
var unencryptedDMs: [HomeScreenRoom] {
|
||||||
Array(sortedRooms.filter { $0.isDirect && !$0.isEncrypted })
|
Array(rooms.filter { $0.isDirect && !$0.isEncrypted })
|
||||||
}
|
}
|
||||||
|
|
||||||
var encryptedDMs: [HomeScreenRoom] {
|
var encryptedDMs: [HomeScreenRoom] {
|
||||||
Array(sortedRooms.filter { $0.isDirect && $0.isEncrypted})
|
Array(rooms.filter { $0.isDirect && $0.isEncrypted})
|
||||||
}
|
}
|
||||||
|
|
||||||
var unencryptedRooms: [HomeScreenRoom] {
|
var unencryptedRooms: [HomeScreenRoom] {
|
||||||
Array(sortedRooms.filter { !$0.isDirect && !$0.isEncrypted })
|
Array(rooms.filter { !$0.isDirect && !$0.isEncrypted })
|
||||||
}
|
}
|
||||||
|
|
||||||
var encryptedRooms: [HomeScreenRoom] {
|
var encryptedRooms: [HomeScreenRoom] {
|
||||||
Array(sortedRooms.filter { !$0.isDirect && $0.isEncrypted })
|
Array(rooms.filter { !$0.isDirect && $0.isEncrypted })
|
||||||
}
|
|
||||||
|
|
||||||
private var filteredRooms: [HomeScreenRoom] {
|
|
||||||
rooms.filter { !$0.isSpace && !$0.isTombstoned }
|
|
||||||
}
|
|
||||||
|
|
||||||
private var sortedRooms: [HomeScreenRoom] {
|
|
||||||
filteredRooms.sorted(by: { ($0.displayName ?? $0.id).lowercased() < ($1.displayName ?? $1.id).lowercased() })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -113,12 +113,11 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func buildOrUpdateRoomFromSummary(_ roomSummary: RoomSummaryProtocol) -> HomeScreenRoom {
|
private func buildOrUpdateRoomFromSummary(_ roomSummary: RoomSummaryProtocol) -> HomeScreenRoom {
|
||||||
|
|
||||||
let lastMessage = lastMessageFromEventBrief(roomSummary.lastMessage)
|
let lastMessage = lastMessageFromEventBrief(roomSummary.lastMessage)
|
||||||
|
|
||||||
guard var room = self.state.rooms.first(where: { $0.id == roomSummary.id }) else {
|
guard var room = self.state.rooms.first(where: { $0.id == roomSummary.id }) else {
|
||||||
return HomeScreenRoom(id: roomSummary.id,
|
return HomeScreenRoom(id: roomSummary.id,
|
||||||
displayName: roomSummary.name,
|
displayName: roomSummary.displayName ?? roomSummary.name,
|
||||||
topic: roomSummary.topic,
|
topic: roomSummary.topic,
|
||||||
lastMessage: lastMessage,
|
lastMessage: lastMessage,
|
||||||
avatar: roomSummary.avatar,
|
avatar: roomSummary.avatar,
|
||||||
@@ -140,11 +139,17 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let senderDisplayName = senderDisplayNameForBrief(eventBrief)
|
||||||
|
|
||||||
if let htmlBody = eventBrief.htmlBody,
|
if let htmlBody = eventBrief.htmlBody,
|
||||||
let lastMessageAttributedString = attributedStringBuilder.fromHTML(htmlBody) {
|
let lastMessageAttributedString = attributedStringBuilder.fromHTML(htmlBody) {
|
||||||
return "\(eventBrief.senderName): \(String(lastMessageAttributedString.characters))"
|
return "\(senderDisplayName): \(String(lastMessageAttributedString.characters))"
|
||||||
} else {
|
} else {
|
||||||
return "\(eventBrief.senderName): \(eventBrief.body)"
|
return "\(senderDisplayName): \(eventBrief.body)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func senderDisplayNameForBrief(_ brief: EventBrief) -> String {
|
||||||
|
brief.senderDisplayName ?? brief.senderId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,23 +115,23 @@ struct RoomCell: View {
|
|||||||
.frame(width: 40, height: 40)
|
.frame(width: 40, height: 40)
|
||||||
}
|
}
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 4.0) {
|
VStack(alignment: .leading, spacing: 2.0) {
|
||||||
Text(roomName(room))
|
Text(roomName(room))
|
||||||
.font(.headline)
|
.foregroundStyle(.primary)
|
||||||
.fontWeight(.regular)
|
|
||||||
|
|
||||||
if let roomTopic = room.topic, roomTopic.count > 0 {
|
if let roomTopic = room.topic, roomTopic.count > 0 {
|
||||||
Text(roomTopic)
|
Text(roomTopic)
|
||||||
.font(.footnote)
|
.font(.footnote.weight(.semibold))
|
||||||
.fontWeight(.bold)
|
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let lastMessage = room.lastMessage {
|
if let lastMessage = room.lastMessage {
|
||||||
Text(lastMessage)
|
Text(lastMessage)
|
||||||
.font(.footnote)
|
.font(.callout)
|
||||||
.fontWeight(.medium)
|
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.padding(.top, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -156,9 +156,16 @@ struct HomeScreen_Previews: PreviewProvider {
|
|||||||
mediaProvider: MockMediaProvider(),
|
mediaProvider: MockMediaProvider(),
|
||||||
attributedStringBuilder: AttributedStringBuilder())
|
attributedStringBuilder: AttributedStringBuilder())
|
||||||
|
|
||||||
let rooms = [MockRoomSummary(displayName: "Alpha"),
|
let eventBrief = EventBrief(eventId: "id",
|
||||||
|
senderId: "senderId",
|
||||||
|
senderDisplayName: "Sender",
|
||||||
|
body: "Some message",
|
||||||
|
htmlBody: nil,
|
||||||
|
date: .now)
|
||||||
|
|
||||||
|
let rooms = [MockRoomSummary(topic: "Topic", displayName: "Alpha"),
|
||||||
MockRoomSummary(displayName: "Beta"),
|
MockRoomSummary(displayName: "Beta"),
|
||||||
MockRoomSummary(displayName: "Omega")]
|
MockRoomSummary(displayName: "Omega", lastMessage: eventBrief)]
|
||||||
|
|
||||||
viewModel.updateWithRoomList(rooms)
|
viewModel.updateWithRoomList(rooms)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// MemberDetailProviderManager.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 01/04/2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class MemberDetailProviderManager {
|
||||||
|
|
||||||
|
private var memberDetailProviders: [String: MemberDetailProviderProtocol] = [:]
|
||||||
|
|
||||||
|
func memberDetailProviderForRoomProxy(_ roomProxy: RoomProxyProtocol) -> MemberDetailProviderProtocol {
|
||||||
|
if let memberDetailProvider = memberDetailProviders[roomProxy.id] {
|
||||||
|
return memberDetailProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
let memberDetailProvider = MemberDetailProvider(roomProxy: roomProxy)
|
||||||
|
memberDetailProviders[roomProxy.id] = memberDetailProvider
|
||||||
|
return memberDetailProvider
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// MemberDetailsProvider.swift
|
// MemberDetailProvider.swift
|
||||||
// ElementX
|
// ElementX
|
||||||
//
|
//
|
||||||
// Created by Stefan Ceriu on 18/03/2022.
|
// Created by Stefan Ceriu on 18/03/2022.
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class MemberDetailsProvider: MemberDetailsProviderProtocol {
|
class MemberDetailProvider: MemberDetailProviderProtocol {
|
||||||
private let roomProxy: RoomProxyProtocol?
|
private let roomProxy: RoomProxyProtocol?
|
||||||
private var memberAvatars = [String: String]()
|
private var memberAvatars = [String: String]()
|
||||||
private var memberDisplayNames = [String: String]()
|
private var memberDisplayNames = [String: String]()
|
||||||
@@ -21,7 +21,7 @@ class MemberDetailsProvider: MemberDetailsProviderProtocol {
|
|||||||
self.memberAvatars[userId]
|
self.memberAvatars[userId]
|
||||||
}
|
}
|
||||||
|
|
||||||
func avatarURLForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailsProviderError>) -> Void) {
|
func avatarURLForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailProviderError>) -> Void) {
|
||||||
guard let roomProxy = roomProxy else {
|
guard let roomProxy = roomProxy else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,7 @@ class MemberDetailsProvider: MemberDetailsProviderProtocol {
|
|||||||
self.memberDisplayNames[userId]
|
self.memberDisplayNames[userId]
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayNameForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailsProviderError>) -> Void) {
|
func displayNameForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailProviderError>) -> Void) {
|
||||||
guard let roomProxy = roomProxy else {
|
guard let roomProxy = roomProxy else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// MemberDetailsProviderProtocol.swift
|
// MemberDetailProviderProtocol.swift
|
||||||
// ElementX
|
// ElementX
|
||||||
//
|
//
|
||||||
// Created by Stefan Ceriu on 18/03/2022.
|
// Created by Stefan Ceriu on 18/03/2022.
|
||||||
@@ -8,16 +8,16 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum MemberDetailsProviderError: Error {
|
enum MemberDetailProviderError: Error {
|
||||||
case invalidRoomProxy
|
case invalidRoomProxy
|
||||||
case failedRetrievingUserAvatarURL
|
case failedRetrievingUserAvatarURL
|
||||||
case failedRetrievingUserDisplayName
|
case failedRetrievingUserDisplayName
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol MemberDetailsProviderProtocol {
|
protocol MemberDetailProviderProtocol {
|
||||||
func avatarURLForUserId(_ userId: String) -> String?
|
func avatarURLForUserId(_ userId: String) -> String?
|
||||||
func avatarURLForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailsProviderError>) -> Void)
|
func avatarURLForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailProviderError>) -> Void)
|
||||||
|
|
||||||
func displayNameForUserId(_ userId: String) -> String?
|
func displayNameForUserId(_ userId: String) -> String?
|
||||||
func displayNameForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailsProviderError>) -> Void)
|
func displayNameForUserId(_ userId: String, completion: @escaping (Result<String?, MemberDetailProviderError>) -> Void)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct EventBrief {
|
struct EventBrief {
|
||||||
let id: String
|
let eventId: String
|
||||||
let senderName: String
|
let senderId: String
|
||||||
|
let senderDisplayName: String?
|
||||||
let body: String
|
let body: String
|
||||||
let htmlBody: String?
|
let htmlBody: String?
|
||||||
let date: Date
|
let date: Date
|
||||||
|
|||||||
@@ -10,20 +10,27 @@ import Foundation
|
|||||||
|
|
||||||
struct EventBriefFactory: EventBriefFactoryProtocol {
|
struct EventBriefFactory: EventBriefFactoryProtocol {
|
||||||
|
|
||||||
func eventBriefForMessage(_ message: RoomMessageProtocol?) -> EventBrief? {
|
private let memberDetailProvider: MemberDetailProviderProtocol
|
||||||
|
|
||||||
|
init(memberDetailProvider: MemberDetailProviderProtocol) {
|
||||||
|
self.memberDetailProvider = memberDetailProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
func eventBriefForMessage(_ message: RoomMessageProtocol?, completion: @escaping ((EventBrief?) -> Void)) {
|
||||||
guard let message = message else {
|
guard let message = message else {
|
||||||
return nil
|
completion(nil)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch message {
|
switch message {
|
||||||
case is ImageRoomMessage:
|
case is ImageRoomMessage:
|
||||||
return nil
|
completion(nil)
|
||||||
case let message as TextRoomMessage:
|
case let message as TextRoomMessage:
|
||||||
return buildEventBrief(message: message, htmlBody: message.htmlBody)
|
buildEventBrief(message: message, htmlBody: message.htmlBody, completion: completion)
|
||||||
case let message as NoticeRoomMessage:
|
case let message as NoticeRoomMessage:
|
||||||
return buildEventBrief(message: message, htmlBody: message.htmlBody)
|
buildEventBrief(message: message, htmlBody: message.htmlBody, completion: completion)
|
||||||
case let message as EmoteRoomMessage:
|
case let message as EmoteRoomMessage:
|
||||||
return buildEventBrief(message: message, htmlBody: message.htmlBody)
|
buildEventBrief(message: message, htmlBody: message.htmlBody, completion: completion)
|
||||||
default:
|
default:
|
||||||
fatalError("Unknown room message.")
|
fatalError("Unknown room message.")
|
||||||
}
|
}
|
||||||
@@ -31,11 +38,26 @@ struct EventBriefFactory: EventBriefFactoryProtocol {
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func buildEventBrief(message: RoomMessageProtocol, htmlBody: String?) -> EventBrief {
|
private func buildEventBrief(message: RoomMessageProtocol, htmlBody: String?, completion: @escaping ((EventBrief?) -> Void)) {
|
||||||
return EventBrief(id: message.id,
|
memberDetailProvider.displayNameForUserId(message.sender) { result in
|
||||||
senderName: message.sender,
|
switch result {
|
||||||
body: message.body,
|
case .success(let displayName):
|
||||||
htmlBody: htmlBody,
|
completion(EventBrief(eventId: message.id,
|
||||||
date: message.originServerTs)
|
senderId: message.sender,
|
||||||
|
senderDisplayName: displayName,
|
||||||
|
body: message.body,
|
||||||
|
htmlBody: htmlBody,
|
||||||
|
date: message.originServerTs))
|
||||||
|
case .failure(let error):
|
||||||
|
MXLog.error("Failed fetching sender display name with error: \(error)")
|
||||||
|
|
||||||
|
completion(EventBrief(eventId: message.id,
|
||||||
|
senderId: message.sender,
|
||||||
|
senderDisplayName: nil,
|
||||||
|
body: message.body,
|
||||||
|
htmlBody: htmlBody,
|
||||||
|
date: message.originServerTs))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol EventBriefFactoryProtocol {
|
protocol EventBriefFactoryProtocol {
|
||||||
func eventBriefForMessage(_ message: RoomMessageProtocol?) -> EventBrief?
|
func eventBriefForMessage(_ message: RoomMessageProtocol?, completion: @escaping ((EventBrief?) -> Void))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,9 @@ class RoomSummary: RoomSummaryProtocol {
|
|||||||
self.mediaProvider = mediaProvider
|
self.mediaProvider = mediaProvider
|
||||||
self.eventBriefFactory = eventBriefFactory
|
self.eventBriefFactory = eventBriefFactory
|
||||||
|
|
||||||
lastMessage = eventBriefFactory.eventBriefForMessage(roomProxy.messages.last)
|
eventBriefFactory.eventBriefForMessage(roomProxy.messages.last) { [weak self] result in
|
||||||
|
self?.lastMessage = result
|
||||||
|
}
|
||||||
|
|
||||||
roomProxy.callbacks.sink { [weak self] callback in
|
roomProxy.callbacks.sink { [weak self] callback in
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
@@ -80,7 +82,9 @@ class RoomSummary: RoomSummaryProtocol {
|
|||||||
|
|
||||||
switch callback {
|
switch callback {
|
||||||
case .updatedMessages:
|
case .updatedMessages:
|
||||||
self.lastMessage = self.eventBriefFactory.eventBriefForMessage(self.roomProxy.messages.last)
|
self.eventBriefFactory.eventBriefForMessage(self.roomProxy.messages.last) { [weak self] result in
|
||||||
|
self?.lastMessage = result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.store(in: &roomUpdateListeners)
|
.store(in: &roomUpdateListeners)
|
||||||
@@ -120,7 +124,9 @@ class RoomSummary: RoomSummaryProtocol {
|
|||||||
|
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
self.lastMessage = self.eventBriefFactory.eventBriefForMessage(self.roomProxy.messages.last)
|
self.eventBriefFactory.eventBriefForMessage(self.roomProxy.messages.last) { [weak self] result in
|
||||||
|
self?.lastMessage = result
|
||||||
|
}
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
MXLog.error("Failed back paginating with error: \(error)")
|
MXLog.error("Failed back paginating with error: \(error)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
private let timelineProvider: RoomTimelineProviderProtocol
|
private let timelineProvider: RoomTimelineProviderProtocol
|
||||||
private let timelineItemFactory: RoomTimelineItemFactory
|
private let timelineItemFactory: RoomTimelineItemFactory
|
||||||
private let mediaProvider: MediaProviderProtocol
|
private let mediaProvider: MediaProviderProtocol
|
||||||
private let memberDetailsProvider: MemberDetailsProviderProtocol
|
private let memberDetailProvider: MemberDetailProviderProtocol
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
@@ -25,11 +25,11 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
init(timelineProvider: RoomTimelineProviderProtocol,
|
init(timelineProvider: RoomTimelineProviderProtocol,
|
||||||
timelineItemFactory: RoomTimelineItemFactory,
|
timelineItemFactory: RoomTimelineItemFactory,
|
||||||
mediaProvider: MediaProviderProtocol,
|
mediaProvider: MediaProviderProtocol,
|
||||||
memberDetailsProvider: MemberDetailsProviderProtocol) {
|
memberDetailProvider: MemberDetailProviderProtocol) {
|
||||||
self.timelineProvider = timelineProvider
|
self.timelineProvider = timelineProvider
|
||||||
self.timelineItemFactory = timelineItemFactory
|
self.timelineItemFactory = timelineItemFactory
|
||||||
self.mediaProvider = mediaProvider
|
self.mediaProvider = mediaProvider
|
||||||
self.memberDetailsProvider = memberDetailsProvider
|
self.memberDetailProvider = memberDetailProvider
|
||||||
|
|
||||||
self.timelineProvider.callbacks.sink { [weak self] callback in
|
self.timelineProvider.callbacks.sink { [weak self] callback in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@@ -152,7 +152,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memberDetailsProvider.avatarURLForUserId(timelineItem.senderId) { result in
|
memberDetailProvider.avatarURLForUserId(timelineItem.senderId) { result in
|
||||||
if case let .success(avatarURL) = result,
|
if case let .success(avatarURL) = result,
|
||||||
let avatarURL = avatarURL {
|
let avatarURL = avatarURL {
|
||||||
self.mediaProvider.loadImageFromURL(avatarURL) { result in
|
self.mediaProvider.loadImageFromURL(avatarURL) { result in
|
||||||
@@ -176,7 +176,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memberDetailsProvider.displayNameForUserId(timelineItem.senderId) { result in
|
memberDetailProvider.displayNameForUserId(timelineItem.senderId) { result in
|
||||||
if case let .success(displayName) = result,
|
if case let .success(displayName) = result,
|
||||||
let displayName = displayName {
|
let displayName = displayName {
|
||||||
guard let index = self.timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
|
guard let index = self.timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ import UIKit
|
|||||||
|
|
||||||
struct RoomTimelineItemFactory {
|
struct RoomTimelineItemFactory {
|
||||||
private let mediaProvider: MediaProviderProtocol
|
private let mediaProvider: MediaProviderProtocol
|
||||||
private let memberDetailsProvider: MemberDetailsProviderProtocol
|
private let memberDetailProvider: MemberDetailProviderProtocol
|
||||||
private let attributedStringBuilder: AttributedStringBuilderProtocol
|
private let attributedStringBuilder: AttributedStringBuilderProtocol
|
||||||
|
|
||||||
init(mediaProvider: MediaProviderProtocol,
|
init(mediaProvider: MediaProviderProtocol,
|
||||||
memberDetailsProvider: MemberDetailsProviderProtocol,
|
memberDetailProvider: MemberDetailProviderProtocol,
|
||||||
attributedStringBuilder: AttributedStringBuilderProtocol) {
|
attributedStringBuilder: AttributedStringBuilderProtocol) {
|
||||||
self.mediaProvider = mediaProvider
|
self.mediaProvider = mediaProvider
|
||||||
self.memberDetailsProvider = memberDetailsProvider
|
self.memberDetailProvider = memberDetailProvider
|
||||||
self.attributedStringBuilder = attributedStringBuilder
|
self.attributedStringBuilder = attributedStringBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildTimelineItemFor(_ roomMessage: RoomMessageProtocol, showSenderDetails: Bool) -> RoomTimelineItemProtocol {
|
func buildTimelineItemFor(_ roomMessage: RoomMessageProtocol, showSenderDetails: Bool) -> RoomTimelineItemProtocol {
|
||||||
let displayName = memberDetailsProvider.displayNameForUserId(roomMessage.sender)
|
let displayName = memberDetailProvider.displayNameForUserId(roomMessage.sender)
|
||||||
let avatarURL = memberDetailsProvider.avatarURLForUserId(roomMessage.sender)
|
let avatarURL = memberDetailProvider.avatarURLForUserId(roomMessage.sender)
|
||||||
let avatarImage = mediaProvider.imageForURL(avatarURL)
|
let avatarImage = mediaProvider.imageForURL(avatarURL)
|
||||||
|
|
||||||
switch roomMessage {
|
switch roomMessage {
|
||||||
|
|||||||
Reference in New Issue
Block a user