Render Room and Message Pills (#3809)

* added a way to render the room and the message

pills, but is WIP

* permalinks now get converted into pills!

* fixed an issue where room address mentions

were not adding a URL properly but a string

* updated tests

* c

* Revert "c"

This reverts commit 5c80252fa23dba7e4d44f2a07fbf1e9500e37c82.

* updated tests

* more tests

* created APIs to get a specific RoomSummary

given the id or the alias

* small mention builder improvement

* pr suggestions
This commit is contained in:
Mauro
2025-02-25 14:46:01 +01:00
committed by GitHub
parent 944fe37fde
commit a2242c63e2
100 changed files with 1017 additions and 247 deletions
@@ -425,14 +425,88 @@ class AttributedStringBuilderTests: XCTestCase {
XCTAssertEqual(numberOfBlockquotes, 3, "Couldn't find all the blockquotes")
}
func testUserMentionAtachment() {
func testUserPermalinkMentionAtachment() {
let string = "https://matrix.to/#/@test:matrix.org"
let attributedStringFromHTML = attributedStringBuilder.fromHTML(string)
XCTAssertNotNil(attributedStringFromHTML?.attachment)
XCTAssertNotNil(attributedStringFromHTML?.link)
XCTAssertEqual(attributedStringFromHTML?.userID, "@test:matrix.org")
XCTAssertEqual(attributedStringFromHTML?.link?.absoluteString, string)
let attributedStringFromPlain = attributedStringBuilder.fromPlain(string)
XCTAssertNotNil(attributedStringFromPlain?.attachment)
XCTAssertNotNil(attributedStringFromHTML?.link)
XCTAssertEqual(attributedStringFromPlain?.userID, "@test:matrix.org")
XCTAssertEqual(attributedStringFromPlain?.link?.absoluteString, string)
}
func testUserIDMentionAtachment() {
let string = "@test:matrix.org"
let attributedStringFromHTML = attributedStringBuilder.fromHTML(string)
XCTAssertNotNil(attributedStringFromHTML?.attachment)
XCTAssertEqual(attributedStringFromHTML?.userID, "@test:matrix.org")
XCTAssertEqual(attributedStringFromHTML?.link?.absoluteString, "https://matrix.to/#/@test:matrix.org")
let attributedStringFromPlain = attributedStringBuilder.fromPlain(string)
XCTAssertNotNil(attributedStringFromPlain?.attachment)
XCTAssertEqual(attributedStringFromPlain?.userID, "@test:matrix.org")
XCTAssertEqual(attributedStringFromPlain?.link?.absoluteString, "https://matrix.to/#/@test:matrix.org")
}
func testRoomIDPermalinkMentionAttachment() {
let string = "https://matrix.to/#/!test:matrix.org"
let attributedStringFromHTML = attributedStringBuilder.fromHTML(string)
XCTAssertNotNil(attributedStringFromHTML?.attachment)
XCTAssertEqual(attributedStringFromHTML?.roomID, "!test:matrix.org")
XCTAssertEqual(attributedStringFromHTML?.link?.absoluteString, string)
let attributedStringFromPlain = attributedStringBuilder.fromPlain(string)
XCTAssertNotNil(attributedStringFromPlain?.attachment)
XCTAssertEqual(attributedStringFromHTML?.roomID, "!test:matrix.org")
XCTAssertEqual(attributedStringFromPlain?.link?.absoluteString, string)
}
func testRoomAliasPermalinkMentionAttachment() {
let string = "https://matrix.to/#/#test:matrix.org"
let attributedStringFromHTML = attributedStringBuilder.fromHTML(string)
XCTAssertNotNil(attributedStringFromHTML?.attachment)
XCTAssertEqual(attributedStringFromHTML?.roomAlias, "#test:matrix.org")
XCTAssertEqual(attributedStringFromHTML?.link?.absoluteString, "https://matrix.to/#/%23test:matrix.org")
let attributedStringFromPlain = attributedStringBuilder.fromPlain(string)
XCTAssertNotNil(attributedStringFromPlain?.attachment)
XCTAssertEqual(attributedStringFromHTML?.roomAlias, "#test:matrix.org")
XCTAssertEqual(attributedStringFromPlain?.link?.absoluteString, "https://matrix.to/#/%23test:matrix.org")
}
func testRoomAliasMentionAttachment() {
let string = "#test:matrix.org"
let attributedStringFromHTML = attributedStringBuilder.fromHTML(string)
XCTAssertNotNil(attributedStringFromHTML?.attachment)
XCTAssertEqual(attributedStringFromHTML?.roomAlias, "#test:matrix.org")
XCTAssertEqual(attributedStringFromHTML?.link?.absoluteString, "https://matrix.to/#/%23test:matrix.org")
let attributedStringFromPlain = attributedStringBuilder.fromPlain(string)
XCTAssertNotNil(attributedStringFromPlain?.attachment)
XCTAssertEqual(attributedStringFromHTML?.roomAlias, "#test:matrix.org")
XCTAssertEqual(attributedStringFromPlain?.link?.absoluteString, "https://matrix.to/#/%23test:matrix.org")
}
func testEventRoomIDPermalinkMentionAttachment() {
let string = "https://matrix.to/#/!test:matrix.org/$test"
let attributedStringFromHTML = attributedStringBuilder.fromHTML(string)
XCTAssertNotNil(attributedStringFromHTML?.attachment)
XCTAssertEqual(attributedStringFromHTML?.eventOnRoomID, .some(.init(roomID: "!test:matrix.org", eventID: "$test")))
XCTAssertEqual(attributedStringFromHTML?.link?.absoluteString, string)
let attributedStringFromPlain = attributedStringBuilder.fromPlain(string)
XCTAssertNotNil(attributedStringFromPlain?.attachment)
XCTAssertEqual(attributedStringFromPlain?.eventOnRoomID, .some(.init(roomID: "!test:matrix.org", eventID: "$test")))
XCTAssertEqual(attributedStringFromPlain?.link?.absoluteString, string)
}
func testEventRoomAliasPermalinkMentionAttachment() {
let string = "https://matrix.to/#/#test:matrix.org/$test"
let attributedStringFromHTML = attributedStringBuilder.fromHTML(string)
XCTAssertNotNil(attributedStringFromHTML?.attachment)
XCTAssertEqual(attributedStringFromHTML?.eventOnRoomAlias, .some(.init(alias: "#test:matrix.org", eventID: "$test")))
XCTAssertEqual(attributedStringFromHTML?.link?.absoluteString, "https://matrix.to/#/%23test:matrix.org/$test")
let attributedStringFromPlain = attributedStringBuilder.fromPlain(string)
XCTAssertNotNil(attributedStringFromPlain?.attachment)
XCTAssertEqual(attributedStringFromPlain?.eventOnRoomAlias, .some(.init(alias: "#test:matrix.org", eventID: "$test")))
XCTAssertEqual(attributedStringFromPlain?.link?.absoluteString, "https://matrix.to/#/%23test:matrix.org/$test")
}
func testUserMentionAtachmentInBlockQuotes() {
@@ -631,7 +705,7 @@ class AttributedStringBuilderTests: XCTestCase {
XCTAssertEqual(foundLink, url)
XCTAssertEqual(foundAttachments, 2)
}
// MARK: - Private
private func checkLinkIn(attributedString: AttributedString?, expectedLink: String, expectedRuns: Int) {
@@ -34,6 +34,7 @@ class HomeScreenRoomTests: XCTestCase {
unreadNotificationsCount: unreadNotificationsCount,
notificationMode: notificationMode,
canonicalAlias: nil,
alternativeAliases: [],
hasOngoingCall: hasOngoingCall,
isMarkedUnread: isMarkedUnread,
isFavourite: false)
+1
View File
@@ -92,6 +92,7 @@ class LoggingTests: XCTestCase {
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: nil,
alternativeAliases: [],
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
+216 -6
View File
@@ -12,7 +12,7 @@ import XCTest
@MainActor
class PillContextTests: XCTestCase {
func testUser() async throws {
func testUser() async {
let id = "@test:matrix.org"
let proxyMock = JoinedRoomProxyMock(.init(name: "Test"))
let subject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([])
@@ -27,7 +27,8 @@ class PillContextTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
@@ -39,10 +40,11 @@ class PillContextTests: XCTestCase {
await Task.yield()
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertNil(context.viewState.image)
XCTAssertEqual(context.viewState.displayText, "@\(name)")
}
func testOwnUser() async throws {
func testOwnUser() {
let id = "@test:matrix.org"
let proxyMock = JoinedRoomProxyMock(.init(name: "Test", ownUserID: id))
let subject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([])
@@ -57,13 +59,15 @@ class PillContextTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body)))
XCTAssertNil(context.viewState.image)
XCTAssertTrue(context.viewState.isOwnMention)
}
func testAllUsers() async throws {
func testAllUsers() {
let avatarURL = URL(string: "https://matrix.jpg")
let id = "test_room"
let displayName = "Test"
@@ -80,10 +84,216 @@ class PillContextTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .allUsers, font: .preferredFont(forTextStyle: .body)))
XCTAssertTrue(context.viewState.isOwnMention)
XCTAssertNil(context.viewState.image)
XCTAssertEqual(context.viewState.displayText, PillConstants.atRoom)
}
func testRoomIDMention() {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
let clientMock = ClientProxyMock(.init())
clientMock.roomSummaryForIdentifierReturnValue = .mock(id: "1", name: "Foundation 🔭🪐🌌")
mockController.roomProxy = proxyMock
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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: clientMock)
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .roomID("1"), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .roomAvatar(.room(id: "1", name: "Foundation 🔭🪐🌌", avatarURL: nil)))
XCTAssertEqual(context.viewState.displayText, "Foundation 🔭🪐🌌")
}
func testRoomIDMentionMissingRoom() {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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 context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .roomID("1"), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .link)
XCTAssertEqual(context.viewState.displayText, "1")
}
func testRoomAliasMention() {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
let clientMock = ClientProxyMock(.init())
clientMock.roomSummaryForAliasReturnValue = .mock(id: "2",
name: "Foundation and Empire",
canonicalAlias: "#foundation-and-empire:matrix.org")
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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: clientMock)
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .roomAlias("#foundation-and-empire:matrix.org"), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .roomAvatar(.room(id: "2", name: "Foundation and Empire", avatarURL: nil)))
XCTAssertEqual(context.viewState.displayText, "Foundation and Empire")
}
func testRoomAliasMentionMissingRoom() {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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 context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .roomAlias("#foundation-and-empire:matrix.org"), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .link)
XCTAssertEqual(context.viewState.displayText, "#foundation-and-empire:matrix.org")
}
func testEventOnRoomIDMention() {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
let clientMock = ClientProxyMock(.init())
clientMock.roomSummaryForIdentifierReturnValue = .mock(id: "1", name: "Foundation 🔭🪐🌌")
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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: clientMock)
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .event(room: .roomID("1")), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .link)
XCTAssertEqual(context.viewState.displayText, L10n.screenRoomEventPill("Foundation 🔭🪐🌌"))
}
func testEventOnRoomIDMentionMissingRoom() {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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 context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .event(room: .roomID("1")), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .link)
XCTAssertEqual(context.viewState.displayText, L10n.screenRoomEventPill("1"))
}
func testEventOnRoomAliasMention() async throws {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
let clientMock = ClientProxyMock(.init())
clientMock.roomSummaryForAliasReturnValue = .mock(id: "2",
name: "Foundation and Empire",
canonicalAlias: "#foundation-and-empire:matrix.org")
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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: clientMock)
let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .event(room: .roomAlias("#foundation-and-empire:matrix.org")), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .link)
XCTAssertEqual(context.viewState.displayText, L10n.screenRoomEventPill("Foundation and Empire"))
}
func testEventOnRoomAliasMentionMissingRoom() async throws {
let proxyMock = JoinedRoomProxyMock(.init())
let mockController = MockTimelineController()
mockController.roomProxy = proxyMock
let mock = TimelineViewModel(roomProxy: proxyMock,
timelineController: mockController,
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 context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .event(room: .roomAlias("#foundation-and-empire:matrix.org")), font: .preferredFont(forTextStyle: .body)))
XCTAssertFalse(context.viewState.isOwnMention)
XCTAssertFalse(context.viewState.isUndefined)
XCTAssertEqual(context.viewState.image, .link)
XCTAssertEqual(context.viewState.displayText, L10n.screenRoomEventPill("#foundation-and-empire:matrix.org"))
}
}
+1
View File
@@ -68,6 +68,7 @@ class RoomSummaryTests: XCTestCase {
unreadNotificationsCount: 0,
notificationMode: nil,
canonicalAlias: nil,
alternativeAliases: [],
hasOngoingCall: false,
isMarkedUnread: false,
isFavourite: false)
+10 -5
View File
@@ -312,7 +312,8 @@ class TimelineViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
return (viewModel, roomProxy, timelineProxy, timelineController)
}
@@ -338,7 +339,8 @@ class TimelineViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
let deferred = deferFulfillment(viewModel.context.$viewState) { value in
value.bindings.readReceiptsSummaryInfo?.orderedReceipts == receipts
@@ -367,7 +369,8 @@ class TimelineViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
XCTAssertEqual(configuration.pinnedEventIDs, viewModel.context.viewState.pinnedEventIDs)
configuration.pinnedEventIDs = ["test1", "test2"]
@@ -394,7 +397,8 @@ class TimelineViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
var deferred = deferFulfillment(viewModel.context.$viewState) { value in
value.canCurrentUserPin
@@ -425,7 +429,8 @@ class TimelineViewModelTests: XCTestCase {
appSettings: ServiceLocator.shared.settings,
analyticsService: ServiceLocator.shared.analytics,
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
timelineControllerFactory: TimelineControllerFactoryMock(.init()))
timelineControllerFactory: TimelineControllerFactoryMock(.init()),
clientProxy: ClientProxyMock(.init()))
}
}