diff --git a/ElementX/Sources/Screens/Timeline/TimelineModels.swift b/ElementX/Sources/Screens/Timeline/TimelineModels.swift index 4b623f9cf769918bfb92c63485d31fb92b8ba71b..8dc0732ec6480d5eebb320a5a9f5279537822947 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineModels.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineModels.swift @@ -117,6 +117,12 @@ struct TimelineViewState: BindableState { /// A closure that updates the associated pill context var pillContextUpdater: (@MainActor (PillContext) -> Void)? + /// A closure that returns the associated room name give its id + var roomNameForIDResolver: (@MainActor (String) -> String?)? + + /// A closure that returns the associated room name give its alias + var roomNameForAliasResolver: (@MainActor (String) -> String?)? + var emojiProvider: EmojiProviderProtocol } diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index b8f58a3f1c9ad87892bc5b040a5d8c45b26a188c..ccb5b8261a6c3032df32b8ccc7dfb5023e675cbb 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -121,6 +121,14 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { self?.pillContextUpdater(pillContext) } + state.roomNameForIDResolver = { [weak self] roomID in + self?.clientProxy.roomSummaryForIdentifier(roomID)?.name + } + + state.roomNameForAliasResolver = { [weak self] alias in + self?.clientProxy.roomSummaryForAlias(alias)?.name + } + state.timelineState.paginationState = timelineController.paginationState buildTimelineViews(timelineItems: timelineController.timelineItems) @@ -935,19 +943,22 @@ extension TimelineViewModel { static let mock = mock(timelineKind: .live) static func mock(timelineKind: TimelineKind = .live, timelineController: MockTimelineController? = nil) -> TimelineViewModel { - TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), - focussedEventID: nil, - timelineController: timelineController ?? MockTimelineController(timelineKind: timelineKind), - mediaProvider: MediaProviderMock(configuration: .init()), - mediaPlayerProvider: MediaPlayerProviderMock(), - voiceMessageMediaManager: VoiceMessageMediaManagerMock(), - userIndicatorController: ServiceLocator.shared.userIndicatorController, - appMediator: AppMediatorMock.default, - appSettings: ServiceLocator.shared.settings, - analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), - timelineControllerFactory: TimelineControllerFactoryMock(.init()), - clientProxy: ClientProxyMock(.init())) + let clientProxyMock = ClientProxyMock(.init()) + clientProxyMock.roomSummaryForAliasReturnValue = .mock(id: "!room:matrix.org", name: "Room") + clientProxyMock.roomSummaryForIdentifierReturnValue = .mock(id: "!room:matrix.org", name: "Room", canonicalAlias: "#room:matrix.org") + return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), + focussedEventID: nil, + timelineController: timelineController ?? MockTimelineController(timelineKind: timelineKind), + mediaProvider: MediaProviderMock(configuration: .init()), + mediaPlayerProvider: MediaPlayerProviderMock(), + voiceMessageMediaManager: VoiceMessageMediaManagerMock(), + userIndicatorController: ServiceLocator.shared.userIndicatorController, + appMediator: AppMediatorMock.default, + appSettings: ServiceLocator.shared.settings, + analyticsService: ServiceLocator.shared.analytics, + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init()), + clientProxy: clientProxyMock) } } diff --git a/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift b/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift index 72604cf307a0e318f27aca6b10c7524a63daba08..de4bf7d1610bab03298a82282adfce89129647a2 100644 --- a/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Replies/TimelineReplyView.swift @@ -200,6 +200,28 @@ struct TimelineReplyView: View { if attributes[.MatrixAllUsersMention] as? Bool == true { attributedString.replaceCharacters(in: range, with: PillUtilities.atRoom) } + + if let roomAlias = attributes[.MatrixRoomAlias] as? String { + let roomName = context.viewState.roomNameForAliasResolver?(roomAlias) + attributedString.replaceCharacters(in: range, with: PillUtilities.roomPillDisplayText(roomName: roomName, rawRoomText: roomAlias)) + } + + if let roomID = attributes[.MatrixRoomID] as? String { + let roomName = context.viewState.roomNameForIDResolver?(roomID) + attributedString.replaceCharacters(in: range, with: PillUtilities.roomPillDisplayText(roomName: roomName, rawRoomText: roomID)) + } + + if let eventOnRoomID = attributes[.MatrixEventOnRoomID] as? EventOnRoomIDAttribute.Value { + let roomID = eventOnRoomID.roomID + let roomName = context.viewState.roomNameForIDResolver?(roomID) + attributedString.replaceCharacters(in: range, with: PillUtilities.eventPillDisplayText(roomName: roomName, rawRoomText: roomID)) + } + + if let eventOnRoomAlias = attributes[.MatrixEventOnRoomAlias] as? EventOnRoomAliasAttribute.Value { + let roomAlias = eventOnRoomAlias.alias + let roomName = context.viewState.roomNameForAliasResolver?(roomAlias) + attributedString.replaceCharacters(in: range, with: PillUtilities.eventPillDisplayText(roomName: roomName, rawRoomText: eventOnRoomAlias.alias)) + } } return attributedString.string } @@ -221,6 +243,30 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview { return attributedString }() + static let attributedStringWithRoomAliasMention = { + var attributedString = AttributedString("to be replaced") + attributedString.roomAlias = "#room:matrix.org" + return attributedString + }() + + static let attributedStringWithRoomIDMention = { + var attributedString = AttributedString("to be replaced") + attributedString.roomID = "!room:matrix.org" + return attributedString + }() + + static let attributedStringWithEventOnRoomIDMention = { + var attributedString = AttributedString("to be replaced") + attributedString.eventOnRoomID = .init(roomID: "!room:matrix.org", eventID: "$event") + return attributedString + }() + + static let attributedStringWithEventOnRoomAliasMention = { + var attributedString = AttributedString("to be replaced") + attributedString.eventOnRoomAlias = .init(alias: "#room:matrix.org", eventID: "$event") + return attributedString + }() + static var previewItems: [TimelineReplyView] { [ TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .notLoaded(eventID: "")), @@ -301,6 +347,22 @@ struct TimelineReplyView_Previews: PreviewProvider, TestablePreview { timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"), eventID: "123", eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithAtRoomMention))))), + TimelineReplyView(placement: .timeline, + timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"), + eventID: "123", + eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithRoomAliasMention))))), + TimelineReplyView(placement: .timeline, + timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"), + eventID: "123", + eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithRoomIDMention))))), + TimelineReplyView(placement: .timeline, + timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"), + eventID: "123", + eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithEventOnRoomIDMention))))), + TimelineReplyView(placement: .timeline, + timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"), + eventID: "123", + eventContent: .message(.notice(.init(body: "", formattedBody: attributedStringWithEventOnRoomAliasMention))))), TimelineReplyView(placement: .timeline, timelineItemReplyDetails: .loaded(sender: .init(id: "", displayName: "Bob"), eventID: "123", diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-en-GB-0.png index f36f6da5b48c40d54450e75f0eeaf795a14c6f91..93e47bbe33043a72d3a3dd2069fa8ffd52046dac 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c20e70f0286b2d7a861d740ff4162d8bb07c2e9ca57cfb3abf997e1ab87e7017 -size 172541 +oid sha256:3b9ef6de65b855b786c3973dd2d19b98500b42288f0fa0ad270fff58cbb2b87b +size 211786 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-pseudo-0.png index b3312eb358ba19704fd30fbc3be254ddb492b437..9a86284d4acb84a172df171b0511f2b522b0efcd 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPad-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:df9e3d57833f3d14ff6521a4946164479a52afb20607cca016dbc51b20fe9439 -size 180282 +oid sha256:1f2f9509ad4173efbc5d31cd36e2090c4ef265556b512ba581d50bc419eef667 +size 219356 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-en-GB-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-en-GB-0.png index 933a0d4b5295e85571ab9bd55309c6ab06971ff9..96b44775debffa04f274d898d8092d72bd63f547 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-en-GB-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-en-GB-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b9fe17381b6d9ee105861a76d50b7c1a78ffaf29fa03cad5c64ff9853a25b01 -size 122809 +oid sha256:c5a3d15cb45d333c8262de10a8503b418b0df28b37922f569e5b5a1e67132450 +size 148474 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-pseudo-0.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-pseudo-0.png index e4001ba362058b01e7bdd31036bf74cc3dafcf89..cba26a08a3e7d7d6a31292eb8024008bef80478b 100644 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-pseudo-0.png +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/timelineReplyView.iPhone-16-pseudo-0.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:911c2bbff4bb91991cc9cc2509fd49442626077a188459a67b2ce35263270c0c -size 128295 +oid sha256:8bfb5dce96a9bab1b0bf4ade63aaeecc90879618e29f7f23defa63e0be473957 +size 153708