From d201ca1ad8404c819d34d46d7a0c5558ffa04882 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 21 Mar 2022 19:35:24 +0200 Subject: [PATCH] Added user display names and switched to using an avatar url directly in the homeScreen viewModel for the current user. --- ElementX.xcodeproj/project.pbxproj | 2 +- .../HomeScreen/HomeScreenCoordinator.swift | 13 ++---- .../Screens/HomeScreen/HomeScreenModels.swift | 2 - .../HomeScreen/HomeScreenViewModel.swift | 14 ++++-- .../HomeScreenViewModelProtocol.swift | 2 - .../Screens/HomeScreen/View/HomeScreen.swift | 3 +- .../Services/Authentication/UserSession.swift | 13 +++++- .../Services/Media/MediaProvider.swift | 16 ------- .../Media/MediaProviderProtocol.swift | 2 - .../Room/Members/MemberDetailsProvider.swift | 35 +++++++++++++++ .../MemberDetailsProviderProtocol.swift | 4 ++ .../Sources/Services/Room/MockRoomProxy.swift | 6 ++- .../Sources/Services/Room/RoomProxy.swift | 20 ++++++++- .../Services/Room/RoomProxyProtocol.swift | 6 ++- .../Timeline/MockRoomTimelineController.swift | 6 +-- .../Timeline/RoomTimelineController.swift | 43 +++++++++++++------ .../EventBasedTimelineItemProtocol.swift | 3 +- .../Items/ImageRoomTimelineItem.swift | 3 +- .../Items/TextRoomTimelineItem.swift | 3 +- .../RoomTimelineItemFactory.swift | 7 ++- .../Views/EventBasedTimelineView.swift | 12 +++++- .../Views/ImageRoomTimelineView.swift | 4 +- .../Views/TextRoomTimelineView.swift | 4 +- 23 files changed, 152 insertions(+), 71 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 505a12212..1b947d36f 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -554,8 +554,8 @@ 18F2BAA327D25B4000DD1988 /* HomeScreen */ = { isa = PBXGroup; children = ( - 18F2BAAB27D25B4000DD1988 /* HomeScreenModels.swift */, 18F2BAA427D25B4000DD1988 /* HomeScreenCoordinator.swift */, + 18F2BAAB27D25B4000DD1988 /* HomeScreenModels.swift */, 18F2BAAA27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift */, 18F2BAAE27D25B4000DD1988 /* HomeScreenViewModel.swift */, 18F2BAAC27D25B4000DD1988 /* View */, diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift index e11f38f34..384d50db3 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenCoordinator.swift @@ -51,7 +51,9 @@ final class HomeScreenCoordinator: Coordinator, Presentable { self.parameters = parameters let userDisplayName = self.parameters.userSession.userDisplayName ?? self.parameters.userSession.userIdentifier - viewModel = HomeScreenViewModel(userDisplayName: userDisplayName, mediaProvider: self.parameters.mediaProvider) + viewModel = HomeScreenViewModel(userDisplayName: userDisplayName, + userAvatarURL: self.parameters.userSession.userAvatarURL, + mediaProvider: self.parameters.mediaProvider) let view = HomeScreen(context: viewModel.context) hostingController = UIHostingController(rootView: view) @@ -62,15 +64,6 @@ final class HomeScreenCoordinator: Coordinator, Presentable { switch result { case .logout: self.completion?(.logout) - case .loadUserAvatar: - self.parameters.mediaProvider.loadCurrentUserAvatar({ result in - switch result { - case .success(let avatar): - self.viewModel.updateWithUserAvatar(avatar) - default: - break - } - }) case .selectRoom(let roomIdentifier): self.completion?(.selectRoom(roomIdentifier: roomIdentifier)) } diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index 3657c3398..8d165fd00 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -19,13 +19,11 @@ import UIKit enum HomeScreenViewModelResult { case logout - case loadUserAvatar case selectRoom(roomIdentifier: String) } enum HomeScreenViewAction { case logout - case loadUserAvatar case loadRoomData(roomIdentifier: String) case selectRoom(roomIdentifier: String) } diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index e9b7b441e..739a077fe 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -37,9 +37,17 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol // MARK: - Setup - init(userDisplayName: String, mediaProvider: MediaProviderProtocol) { + init(userDisplayName: String, userAvatarURL: String?, mediaProvider: MediaProviderProtocol) { self.mediaProvider = mediaProvider super.init(initialViewState: HomeScreenViewState(userDisplayName: userDisplayName, isLoadingRooms: true)) + + if let userAvatarURL = userAvatarURL { + mediaProvider.loadImageFromURL(userAvatarURL) { [weak self] result in + if case let .success(avatar) = result { + self?.state.userAvatar = avatar + } + } + } } // MARK: - Public @@ -50,8 +58,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol self.completion?(.logout) case .loadRoomData(let roomIdentifier): self.loadRoomDataForIdentifier(roomIdentifier) - case .loadUserAvatar: - self.completion?(.loadUserAvatar) case .selectRoom(let roomIdentifier): self.completion?(.selectRoom(roomIdentifier: roomIdentifier)) } @@ -118,7 +124,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol return } - room.loadDisplayName { [weak self] result in + room.displayName { [weak self] result in guard let self = self else { return } switch result { diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift index 2c72ecb57..5ba27a652 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModelProtocol.swift @@ -23,6 +23,4 @@ protocol HomeScreenViewModelProtocol { var context: HomeScreenViewModelType.Context { get } func updateWithRoomList(_ roomList: [RoomProxyProtocol]) - - func updateWithUserAvatar(_ avatar: UIImage?) } diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift index 3f3a68bcf..316f414d5 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift @@ -79,8 +79,6 @@ struct HomeScreen: View { .scaledToFill() .frame(width: 40, height: 40, alignment: .center) .mask(Circle()) - } else { - let _ = context.send(viewAction: .loadUserAvatar) } Text("Hello, \(context.viewState.userDisplayName)!") .font(.subheadline) @@ -154,6 +152,7 @@ struct RoomCell: View { struct HomeScreen_Previews: PreviewProvider { static var previews: some View { let viewModel = HomeScreenViewModel(userDisplayName: "Johnny Appleseed", + userAvatarURL: nil, mediaProvider: MockMediaProvider()) let rooms = [MockRoomProxy(displayName: "Alfa"), diff --git a/ElementX/Sources/Services/Authentication/UserSession.swift b/ElementX/Sources/Services/Authentication/UserSession.swift index fac40baf6..73643f075 100644 --- a/ElementX/Sources/Services/Authentication/UserSession.swift +++ b/ElementX/Sources/Services/Authentication/UserSession.swift @@ -67,7 +67,16 @@ class UserSession: ClientDelegate { do { return try client.displayName() } catch { - MXLog.error("Failed retrieving room info with error: \(error)") + MXLog.error("Failed retrieving the user's display name with error: \(error)") + return nil + } + } + + var userAvatarURL: String? { + do { + return try client.avatarUrl() + } catch { + MXLog.error("Failed retrieving the user's avatar URL with error: \(error)") return nil } } @@ -93,7 +102,7 @@ class UserSession: ClientDelegate { func fetchRoomList(_ completion: @escaping ([RoomProxy]) -> Void) { DispatchQueue.global(qos: .background).async { - let rooms = self.client.conversations().map { + let rooms = self.client.rooms().map { return RoomProxy(room: $0, messageFactory: RoomMessageFactory()) } diff --git a/ElementX/Sources/Services/Media/MediaProvider.swift b/ElementX/Sources/Services/Media/MediaProvider.swift index 9461823d5..59cd858cb 100644 --- a/ElementX/Sources/Services/Media/MediaProvider.swift +++ b/ElementX/Sources/Services/Media/MediaProvider.swift @@ -21,22 +21,6 @@ struct MediaProvider: MediaProviderProtocol { self.processingQueue = DispatchQueue(label: "MediaProviderProcessingQueue") } - func loadCurrentUserAvatar(_ completion: @escaping (Result) -> Void) { - processingQueue.async { - do { - let imageData = try client.avatar() - DispatchQueue.main.async { - completion(.success(UIImage(data: Data(bytes: imageData, count: imageData.count)))) - } - } catch { - MXLog.error("Failed retrieving image with error: \(error)") - DispatchQueue.main.async { - completion(.failure(.failedRetrievingImage)) - } - } - } - } - func imageForURL(_ url: String?) -> UIImage? { guard let url = url else { return nil diff --git a/ElementX/Sources/Services/Media/MediaProviderProtocol.swift b/ElementX/Sources/Services/Media/MediaProviderProtocol.swift index 9806dfec3..03ff93c13 100644 --- a/ElementX/Sources/Services/Media/MediaProviderProtocol.swift +++ b/ElementX/Sources/Services/Media/MediaProviderProtocol.swift @@ -15,8 +15,6 @@ enum MediaProviderError: Error { } protocol MediaProviderProtocol { - func loadCurrentUserAvatar(_ completion: @escaping (Result) -> Void) - func imageForURL(_ url: String?) -> UIImage? func loadImageFromURL(_ url: String, _ completion: @escaping (Result) -> Void) diff --git a/ElementX/Sources/Services/Room/Members/MemberDetailsProvider.swift b/ElementX/Sources/Services/Room/Members/MemberDetailsProvider.swift index 6c093c802..747fe2007 100644 --- a/ElementX/Sources/Services/Room/Members/MemberDetailsProvider.swift +++ b/ElementX/Sources/Services/Room/Members/MemberDetailsProvider.swift @@ -12,6 +12,7 @@ class MemberDetailsProvider: MemberDetailsProviderProtocol { private let roomProxy: RoomProxyProtocol? private let processingQueue = DispatchQueue(label: "MemberDetailsProviderProcessingQueue") private var memberAvatars = [String: String]() + private var memberDisplayNames = [String: String]() init(roomProxy: RoomProxyProtocol) { self.roomProxy = roomProxy @@ -50,4 +51,38 @@ class MemberDetailsProvider: MemberDetailsProviderProtocol { }) } } + + func displayNameForUserId(_ userId: String) -> String? { + self.memberDisplayNames[userId] + } + + func displayNameForUserId(_ userId: String, completion: @escaping (Result) -> Void) { + guard let roomProxy = roomProxy else { + return + } + + if let avatarURL = displayNameForUserId(userId) { + completion(.success(avatarURL)) + } + + processingQueue.async { + roomProxy.displayNameForUserId(userId, completion: { [weak self] result in + guard let self = self else { + return + } + + switch result { + case .success(let displayName): + DispatchQueue.main.async { + self.memberDisplayNames[userId] = displayName + completion(.success(displayName)) + } + case .failure: + DispatchQueue.main.async { + completion(.failure(.failedRetrievingUserDisplayName)) + } + } + }) + } + } } diff --git a/ElementX/Sources/Services/Room/Members/MemberDetailsProviderProtocol.swift b/ElementX/Sources/Services/Room/Members/MemberDetailsProviderProtocol.swift index 3fb2c81f5..b42590f46 100644 --- a/ElementX/Sources/Services/Room/Members/MemberDetailsProviderProtocol.swift +++ b/ElementX/Sources/Services/Room/Members/MemberDetailsProviderProtocol.swift @@ -11,9 +11,13 @@ import Foundation enum MemberDetailsProviderError: Error { case invalidRoomProxy case failedRetrievingUserAvatarURL + case failedRetrievingUserDisplayName } protocol MemberDetailsProviderProtocol { func avatarURLForUserId(_ userId: String) -> String? func avatarURLForUserId(_ userId: String, completion: @escaping (Result) -> Void) + + func displayNameForUserId(_ userId: String) -> String? + func displayNameForUserId(_ userId: String, completion: @escaping (Result) -> Void) } diff --git a/ElementX/Sources/Services/Room/MockRoomProxy.swift b/ElementX/Sources/Services/Room/MockRoomProxy.swift index 836f5c1c0..0b32234c4 100644 --- a/ElementX/Sources/Services/Room/MockRoomProxy.swift +++ b/ElementX/Sources/Services/Room/MockRoomProxy.swift @@ -26,7 +26,7 @@ struct MockRoomProxy: RoomProxyProtocol { var callbacks = PassthroughSubject() - func loadDisplayName(_ completion: @escaping (Result) -> Void) { + func displayName(_ completion: @escaping (Result) -> Void) { completion(.success(displayName)) } @@ -41,4 +41,8 @@ struct MockRoomProxy: RoomProxyProtocol { func avatarURLForUserId(_ userId: String, completion: @escaping (Result) -> Void) { } + + func displayNameForUserId(_ userId: String, completion: @escaping (Result) -> Void) { + + } } diff --git a/ElementX/Sources/Services/Room/RoomProxy.swift b/ElementX/Sources/Services/Room/RoomProxy.swift index 36cb92008..dbd105a9d 100644 --- a/ElementX/Sources/Services/Room/RoomProxy.swift +++ b/ElementX/Sources/Services/Room/RoomProxy.swift @@ -100,13 +100,29 @@ class RoomProxy: RoomProxyProtocol, Equatable { } } catch { DispatchQueue.main.async { - completion(.failure(RoomProxyError.failedRetrievingDisplayName)) + completion(.failure(RoomProxyError.failedRetrievingMemberAvatarURL)) } } } } - func loadDisplayName(_ completion: @escaping (Result) -> Void) { + func displayNameForUserId(_ userId: String, completion: @escaping (Result) -> Void) { + processingQueue.async { + do { + let displayName = try self.room.memberDisplayName(userId: userId) + + DispatchQueue.main.async { + completion(.success(displayName)) + } + } catch { + DispatchQueue.main.async { + completion(.failure(RoomProxyError.failedRetrievingMemberDisplayName)) + } + } + } + } + + func displayName(_ completion: @escaping (Result) -> Void) { processingQueue.async { do { let displayName = try self.room.displayName() diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index 27227beef..554d03f0a 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -12,6 +12,8 @@ enum RoomProxyError: Error { case failedRetrievingDisplayName case failedRetrievingAvatar case backwardStreamNotAvailable + case failedRetrievingMemberAvatarURL + case failedRetrievingMemberDisplayName } enum RoomProxyCallback { @@ -33,9 +35,11 @@ protocol RoomProxyProtocol { var avatarURL: String? { get } + func displayName(_ completion: @escaping (Result) -> Void) + func avatarURLForUserId(_ userId: String, completion: @escaping (Result) -> Void) - func loadDisplayName(_ completion: @escaping (Result) -> Void) + func displayNameForUserId(_ userId: String, completion: @escaping (Result) -> Void) func paginateBackwards(count: UInt, callback: ((Result<[RoomMessageProtocol], RoomProxyError>) -> Void)?) diff --git a/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift index 317372d7d..45e7d443e 100644 --- a/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/MockRoomTimelineController.swift @@ -14,10 +14,10 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { let callbacks = PassthroughSubject() var timelineItems: [RoomTimelineItemProtocol] = [SeparatorRoomTimelineItem(id: UUID().uuidString, text: "Yesterday"), - TextRoomTimelineItem(id: UUID().uuidString, text: "You rock!", timestamp: "10:10 AM", shouldShowSenderDetails: true, sender: "Alice"), - TextRoomTimelineItem(id: UUID().uuidString, text: "You also rule!", timestamp: "10:11 AM", shouldShowSenderDetails: false, sender: "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, text: "You too!", timestamp: "5 PM", shouldShowSenderDetails: true, sender: "Bob")] + TextRoomTimelineItem(id: UUID().uuidString, text: "You too!", timestamp: "5 PM", shouldShowSenderDetails: true, senderId: "Bob")] func paginateBackwards(_ count: UInt, callback: ((Result) -> Void)) { callbacks.send(.updatedTimelineItems) diff --git a/ElementX/Sources/Services/Timeline/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/RoomTimelineController.swift index 6baa6b3dc..318081d11 100644 --- a/ElementX/Sources/Services/Timeline/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/RoomTimelineController.swift @@ -58,7 +58,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } if let item = timelineItem as? EventBasedTimelineItemProtocol { - loadAvatarForTimelineItem(item) + loadUserAvatarForTimelineItem(item) + loadUserDisplayNameForTimelineItem(item) } switch timelineItem { @@ -110,13 +111,11 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } private func loadImageForTimelineItem(_ timelineItem: ImageRoomTimelineItem) { - var item = timelineItem - - if item.image != nil { + if timelineItem.image != nil { return } - guard let url = item.url else { + guard let url = timelineItem.url else { return } @@ -126,7 +125,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } if case let .success(image) = result { - guard let index = self.timelineItems.firstIndex(where: { $0.id == timelineItem.id }) else { + guard let index = self.timelineItems.firstIndex(where: { $0.id == timelineItem.id }), + var item = self.timelineItems[index] as? ImageRoomTimelineItem else { return } @@ -137,19 +137,18 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } - private func loadAvatarForTimelineItem(_ timelineItem: EventBasedTimelineItemProtocol) { - var item = timelineItem - - if item.shouldShowSenderDetails == false { + private func loadUserAvatarForTimelineItem(_ timelineItem: EventBasedTimelineItemProtocol) { + if timelineItem.shouldShowSenderDetails == false { return } - memberDetailsProvider.avatarURLForUserId(timelineItem.sender) { result in + memberDetailsProvider.avatarURLForUserId(timelineItem.senderId) { result in if case let .success(avatarURL) = result, let avatarURL = avatarURL { self.mediaProvider.loadImageFromURL(avatarURL) { result in if case let .success(image) = result { - guard let index = self.timelineItems.firstIndex(where: { $0.id == timelineItem.id }) else { + guard let index = self.timelineItems.firstIndex(where: { $0.id == timelineItem.id }), + var item = self.timelineItems[index] as? EventBasedTimelineItemProtocol else { return } @@ -161,4 +160,24 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } } + + private func loadUserDisplayNameForTimelineItem(_ timelineItem: EventBasedTimelineItemProtocol) { + if timelineItem.shouldShowSenderDetails == false { + return + } + + memberDetailsProvider.displayNameForUserId(timelineItem.senderId) { result in + if case let .success(displayName) = result, + let displayName = displayName { + guard let index = self.timelineItems.firstIndex(where: { $0.id == timelineItem.id }), + var item = self.timelineItems[index] as? EventBasedTimelineItemProtocol else { + return + } + + item.senderDisplayName = displayName + self.timelineItems[index] = item + self.callbacks.send(.updatedTimelineItem(timelineItem.id)) + } + } + } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedTimelineItemProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedTimelineItemProtocol.swift index 971819b89..891f0f002 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedTimelineItemProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/EventBasedTimelineItemProtocol.swift @@ -14,6 +14,7 @@ protocol EventBasedTimelineItemProtocol: RoomTimelineItemProtocol { var timestamp: String { get } var shouldShowSenderDetails: Bool { get } - var sender: String { get } + var senderId: String { get } + var senderDisplayName: String? { get set } var senderAvatar: UIImage? { get set } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/ImageRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/ImageRoomTimelineItem.swift index 9511e6bd2..b375acbf1 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/ImageRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/ImageRoomTimelineItem.swift @@ -15,7 +15,8 @@ struct ImageRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equa let timestamp: String let shouldShowSenderDetails: Bool - let sender: String + let senderId: String + var senderDisplayName: String? var senderAvatar: UIImage? let url: String? diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Items/TextRoomTimelineItem.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Items/TextRoomTimelineItem.swift index 367675fc0..4c13fc8ec 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Items/TextRoomTimelineItem.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Items/TextRoomTimelineItem.swift @@ -15,6 +15,7 @@ struct TextRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Equat let timestamp: String let shouldShowSenderDetails: Bool - let sender: String + let senderId: String + var senderDisplayName: String? var senderAvatar: UIImage? } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift index aac9e09b7..c63e6ec96 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/RoomTimelineItemFactory.swift @@ -21,6 +21,7 @@ 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) @@ -30,14 +31,16 @@ struct RoomTimelineItemFactory { text: message.content, timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened), shouldShowSenderDetails: showSenderDetails, - sender: message.sender, + senderId: message.sender, + senderDisplayName: displayName, senderAvatar: avatarImage) case let message as ImageRoomMessage: return ImageRoomTimelineItem(id: message.id, text: message.content, timestamp: message.originServerTs.formatted(date: .omitted, time: .shortened), shouldShowSenderDetails: showSenderDetails, - sender: message.sender, + senderId: message.sender, + senderDisplayName: displayName, senderAvatar: avatarImage, url: message.url, image: mediaProvider.imageForURL(message.url)) diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift index 4d405b030..d50111fcb 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Views/EventBasedTimelineView.swift @@ -16,7 +16,7 @@ struct EventBasedTimelineView: View { if timelineItem.shouldShowSenderDetails { HStack { avatar - Text(timelineItem.sender) + Text(timelineItem.senderDisplayName ?? timelineItem.senderId) .font(.footnote) .bold() Spacer() @@ -35,10 +35,18 @@ struct EventBasedTimelineView: View { .scaledToFill() .overlay(Circle().stroke(Color(.sRGB, red: 0.05, green: 0.74, blue: 0.55, opacity: 1.0))) } else { - PlaceholderAvatarImage(firstCharacter: String(timelineItem.sender.prefix(2).suffix(1)).uppercased()) + PlaceholderAvatarImage(firstCharacter: String(firstLetter)) } } .clipShape(Circle()) .frame(width: 44.0, height: 44.0) } + + private var firstLetter: String { + if let senderDisplayName = timelineItem.senderDisplayName { + return senderDisplayName.prefix(1).uppercased() + } else { + return timelineItem.senderId.prefix(2).suffix(1).uppercased() + } + } } diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Views/ImageRoomTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Views/ImageRoomTimelineView.swift index 8c776946f..57627a59f 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Views/ImageRoomTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Views/ImageRoomTimelineView.swift @@ -40,7 +40,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider { text: "Some image", timestamp: "Now", shouldShowSenderDetails: false, - sender: "Bob", + senderId: "Bob", url: nil, image: UIImage(systemName: "photo")) ImageRoomTimelineView(timelineItem: timelineItem) @@ -49,7 +49,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider { text: "Some other image", timestamp: "Now", shouldShowSenderDetails: false, - sender: "Bob", + senderId: "Bob", url: nil, image: nil) ImageRoomTimelineView(timelineItem: timelineItem) diff --git a/ElementX/Sources/Services/Timeline/TimelineItems/Views/TextRoomTimelineView.swift b/ElementX/Sources/Services/Timeline/TimelineItems/Views/TextRoomTimelineView.swift index 3de975973..969922a8e 100644 --- a/ElementX/Sources/Services/Timeline/TimelineItems/Views/TextRoomTimelineView.swift +++ b/ElementX/Sources/Services/Timeline/TimelineItems/Views/TextRoomTimelineView.swift @@ -34,14 +34,14 @@ struct TextRoomTimelineView_Previews: PreviewProvider { 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, - sender: "Bob") + senderId: "Bob") TextRoomTimelineView(timelineItem: timelineItem) let timelineItem = TextRoomTimelineItem(id: UUID().uuidString, text: "Some other text", timestamp: "Later", shouldShowSenderDetails: true, - sender: "Anne") + senderId: "Anne") TextRoomTimelineView(timelineItem: timelineItem) } .padding()