diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index dbbd46075..9ac388573 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -66,7 +66,6 @@ 095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */; }; 09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; }; 09AAF04B27732046C755D914 /* SoftLogoutViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */; }; - 09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */; }; 09D3D7D115318CAD131B4FE7 /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57084488B03BDB33C7B7CA0E /* ResolveVerifiedUserSendFailureScreenViewModelTests.swift */; }; 0A0625A271EE5B06D2AAA069 /* HomeScreenSlidingSyncMigrationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4691B8DE1D51DE152680098A /* HomeScreenSlidingSyncMigrationBanner.swift */; }; 0A194F5E70B5A628C1BF4476 /* AdvancedSettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4999B5FD50AED7CB0F590FF8 /* AdvancedSettingsScreenModels.swift */; }; @@ -221,7 +220,6 @@ 2A864BB12A8501B47805D828 /* AuthenticationFlowCoordinatorUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 295E28C3B9EAADF519BF2F44 /* AuthenticationFlowCoordinatorUITests.swift */; }; 2AAB2A77F1762A2648078A30 /* InteractiveQuickLook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 638A81B97D51591D0FCFA598 /* InteractiveQuickLook.swift */; }; 2AB9D4146C8748CF1D007B67 /* test_pdf.pdf in Resources */ = {isa = PBXBuildFile; fileRef = BE98688578F8B0541D853695 /* test_pdf.pdf */; }; - 2ABF11717C64054CEF2819A3 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */; }; 2AED12987603157C32C2114D /* TimelineBubbleLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5D8FEB1FED10E995CB002F7 /* TimelineBubbleLayout.swift */; }; 2B97BCE72D86645F1485C976 /* RoomDirectorySearchMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 894EE8F5B399A165BA2A6634 /* RoomDirectorySearchMock.swift */; }; 2BA59D0AEFB4B82A2EC2A326 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 50009897F60FAE7D63EF5E5B /* Kingfisher */; }; @@ -230,6 +228,7 @@ 2BBC0EB1E07963810A5D7423 /* ReadMarkerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 012A284622B32052015F1F89 /* ReadMarkerRoomTimelineView.swift */; }; 2BBE320EE426A347AAE5C7DA /* IdentityConfirmationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00AFC5F08734C2EA4EE79C59 /* IdentityConfirmationScreen.swift */; }; 2BC579CB5CE90CFE07CA0955 /* EditRoomAddressScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41656BC6267D55C56A2AAC08 /* EditRoomAddressScreenCoordinator.swift */; }; + 2BFA4C6D5B3D327B02C66AB0 /* TimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4216C12C0369A8AB059EDE9 /* TimelineController.swift */; }; 2C4C750D0039AFABDF24236C /* TemplateScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */; }; 2C5E832434EE94E21AB3B238 /* EmojiPickerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EAE3E9D5EF4A6D5D9C6CFD /* EmojiPickerScreenViewModel.swift */; }; 2CA61BB208CD82EBDB58CD13 /* VideoRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0CBEAB5F796BEFBAF7BB6A /* VideoRoomTimelineView.swift */; }; @@ -287,7 +286,6 @@ 384D6B9A7DFD7260139D6852 /* UITestsNotificationCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */; }; 38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; }; 386720B603F87D156DB01FB2 /* VoiceMessageMediaManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40076C770A5FB83325252973 /* VoiceMessageMediaManager.swift */; }; - 38896D54D6D675534E606195 /* RoomTimelineControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */; }; 388D39ED9FE1122EA6D76BF2 /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BC84BA0AF11C2128D58ABD /* Common.swift */; }; 3895969759E68FAB90C63EF7 /* ElementCallServiceConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 406C90AF8C3E98DF5D4E5430 /* ElementCallServiceConstants.swift */; }; 38CC67C7673FA97C21CCD5B5 /* WebRegistrationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B065EC39C99C1303A101C1C /* WebRegistrationScreen.swift */; }; @@ -399,6 +397,7 @@ 4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; }; 4E22086585CB3B35FEEFBBB9 /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FA991289149D31F4286747 /* UserPreference.swift */; }; 4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */; }; + 4E4EF97B9F9CEFAC726BA72F /* TimelineProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EACAFB3F3E017060F9F1C5 /* TimelineProviderMock.swift */; }; 4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.swift */; }; 4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */; }; 4EA1CE0E88EA68E862FF0EA2 /* NotificationSettingsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */; }; @@ -418,6 +417,7 @@ 51B3B19FA5F91B455C807BA7 /* RoomPollsHistoryScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E964AF2DFEB31E2B799999F /* RoomPollsHistoryScreenModels.swift */; }; 523C6800ED85D5810CF18C19 /* OIDCAccountSettingsPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D737F4672021D0A7D218CD /* OIDCAccountSettingsPresenter.swift */; }; 52473A4D7B1FBD4CD1E770C8 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; }; + 530C2238E40F71223327FC95 /* MockTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA8082E26C77A2C587B34B3 /* MockTimelineController.swift */; }; 5341D48F833E3E30F16FA2A3 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2910422CB628D3B2BBE47449 /* SeparatorRoomTimelineView.swift */; }; 5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; }; 53A55748D5F587C9061F98BF /* ServerConfigurationScreenViewStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277C20CDD5B64510401B6D0D /* ServerConfigurationScreenViewStateTests.swift */; }; @@ -470,6 +470,7 @@ 5DB4334CBBA142376FF5FFEC /* preview_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 200626E8353AB2729444F991 /* preview_image.jpg */; }; 5DD0EF30070DC0A82C5CCD33 /* RoomMembersListManageMemberSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */; }; 5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */; }; + 5EC046E41755C095DAB1C3FF /* TimelineProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8C9BBB729C941BEE0E2A63 /* TimelineProviderProtocol.swift */; }; 5EDBDE802761B5ECB54E6787 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2711E5996016ABD6EAAEB58A /* LogLevel.swift */; }; 5EE1D4E316D66943E97FDCF2 /* BloomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BEB970F500BFB248443FA1 /* BloomView.swift */; }; 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */; }; @@ -497,6 +498,7 @@ 6386EA3C898AD1A4BC1DC8A5 /* TimelineMediaPreviewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD40B92FCF20165658296AD /* TimelineMediaPreviewModifier.swift */; }; 63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */; }; 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */; }; + 63FD7DBE7BBA149B4B8B99D7 /* TimelineControllerFactoryMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0F5CC38803B8382D2C63222 /* TimelineControllerFactoryMock.swift */; }; 642DF13C49ED4121C148230E /* TestablePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E227F34BE43B08E098796E /* TestablePreview.swift */; }; 6448F8D1D3CA4CD27BB4CADD /* RoomMemberProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F36C5D9B37E50915ECBD3EE /* RoomMemberProxy.swift */; }; 64AB99285DC4437C0DDE9585 /* MenuSheetLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49ABAB186CF00B15C5521D04 /* MenuSheetLabelStyle.swift */; }; @@ -600,7 +602,6 @@ 77920AFA8091AC6B9F190C90 /* Signposter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 752A0EB49BF5BCEA37EDF7A3 /* Signposter.swift */; }; 77BB228AEA861E50FFD6A228 /* HomeScreenEmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FEA560929DD73FFEF8C3DF /* HomeScreenEmptyStateView.swift */; }; 77C1A2F49CD90D3EFDF376E5 /* MapTilerURLBuildersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */; }; - 77D7DAA41AAB36800C1F2E2D /* RoomTimelineProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */; }; 77E33FF0E4A50B555BF3A8AA /* AudioFileEventsTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC9044BE0E4A66F5B963E834 /* AudioFileEventsTimelineView.swift */; }; 77FACC29F98FE2E65BBB6A5F /* ServerSelectionUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */; }; 77FB08C303F4C74C0E8577E2 /* TimelineMediaPreviewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A2BB38DF61F5100B8723112 /* TimelineMediaPreviewModels.swift */; }; @@ -638,10 +639,10 @@ 7D261B5119E78CC8E771CA15 /* GlobalSearchScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74653BE903970C0E36867D46 /* GlobalSearchScreenCoordinator.swift */; }; 7D58B4F46CAA9A7C3E4C6A30 /* UserDetailsEditScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88410BD213FDF9B28E8B671F /* UserDetailsEditScreen.swift */; }; 7D6DC832DE7A3DE874E2E9BC /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = BB111AE9D390233CDD2C7FD5 /* MatrixRustSDK */; }; + 7DCFC31B49BDD2BC32184E58 /* TimelineControllerFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C0D5AE752AF87DA0A920812 /* TimelineControllerFactory.swift */; }; 7E2BB42805C59DB57E95610F /* PillView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7773CBFDBD458E0B7E270507 /* PillView.swift */; }; 7E43FBB918AAC136034F2758 /* test_image.png in Resources */ = {isa = PBXBuildFile; fileRef = 810133CF215075C285FC6F3A /* test_image.png */; }; 7E91BAC17963ED41208F489B /* UserSessionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E8BDC092D817B68CD9040C5 /* UserSessionStore.swift */; }; - 7ECF12D5DCD69F67BD3E3842 /* RoomTimelineControllerFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18FE0CDF1FFA92EA7EE17B0B /* RoomTimelineControllerFactoryProtocol.swift */; }; 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */; }; 7F7EA51A9A43125A8CB6AC90 /* NotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46D560DDA3B20C82766ACFAD /* NotificationSettingsScreenViewModel.swift */; }; 7F825CBD857D65DC986087BA /* NoticeRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F54FA7C5CB7B342EF9B9B2F /* NoticeRoomTimelineView.swift */; }; @@ -788,7 +789,6 @@ 9B356742E035D90A8BB5CABE /* ProposedViewSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */; }; 9B872FF37DBE6BE054903831 /* MediaUploadPreviewScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D54E12B98252F6C527E31FEE /* MediaUploadPreviewScreenViewModelProtocol.swift */; }; 9BB91CABB10D8FE90C491BCD /* StaticLocationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C833673B334A0651AB46F30B /* StaticLocationScreenViewModelTests.swift */; }; - 9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */; }; 9C4EC28A921486B1775D7F8C /* IdentityConfirmedScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 307702DD66E7DDCDD9214784 /* IdentityConfirmedScreen.swift */; }; 9C55746D8F6A3E35CFCF4A7A /* AuthenticationStartLogo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 598F01EBD0C4CC550C644418 /* AuthenticationStartLogo.swift */; }; 9C63171267E22FEB288EC860 /* RoomHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1627F2D56477BD331F6D732C /* RoomHeaderView.swift */; }; @@ -896,13 +896,12 @@ B20484642B41C2D76238BAAA /* test_animated_image.gif in Resources */ = {isa = PBXBuildFile; fileRef = 53FD6D3D38F556CEAA280C58 /* test_animated_image.gif */; }; B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */; }; B245583C63F8F90357B87FAE /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = A2AE110B053B55E38F8D10C7 /* KZFileWatchers */; }; - B272E5D1DE8BDA87A6B7A696 /* RoomTimelineProviderMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */; }; B2F8E01ABA1BA30265B4ECBE /* RoundedCornerShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */; }; B2FDF69AC0C316F12142F91A /* RoomMembershipDetailsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12B09A94C519227264A41208 /* RoomMembershipDetailsProxy.swift */; }; - B31E5493C99381D4E204438B /* RoomTimelineControllerFactoryMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D479DF730528153665E5782E /* RoomTimelineControllerFactoryMock.swift */; }; B3D652AA1654270742072FB3 /* DeveloperOptionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86A6F283BC574FDB96ABBB07 /* DeveloperOptionsScreenViewModel.swift */; }; B402708F8728DD0DB7C324E2 /* StartChatScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */; }; B444F9C184A377C1B481F07F /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E992D7B8BE54B2AB454613AF /* XCUIElement.swift */; }; + B47213F07A67CE3A8D49CEC9 /* TimelineControllerFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AC1E3FE9B59EA094867863E /* TimelineControllerFactoryProtocol.swift */; }; B4A0C69370E6008A971463E7 /* BugReportScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4C89820BB2B88D4EA28131C /* BugReportScreenViewModelProtocol.swift */; }; B4AAB3257A83B73F53FB2689 /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */; }; B5321A1F5B26A0F3EC54909E /* CollapsibleFlowLayoutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC5F5209279A752D98AAC4B2 /* CollapsibleFlowLayoutTests.swift */; }; @@ -1078,6 +1077,7 @@ D97C782FE0005995C36FA04A /* portrait_test_video.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = E5E7D4EE7CA295E5039FDA21 /* portrait_test_video.mp4 */; }; D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; }; D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.swift */; }; + DA03B4F28C4D248EECE3429F /* TimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371B248460BD1A3F20318137 /* TimelineProvider.swift */; }; DA10C99BA43A0F1E732F6274 /* LogLevel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2711E5996016ABD6EAAEB58A /* LogLevel.swift */; }; DA7E867F5EAFF8E20B2EE3B6 /* SecureBackupScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3D16709ADD4F4BCC710B1E /* SecureBackupScreenModels.swift */; }; DAF63A9CF9932CA8F6830F11 /* ShareExtensionModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */; }; @@ -1140,7 +1140,6 @@ E79D79CDAFE8BEBCC3AECA54 /* AppLockScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08283301736A6FE9D558B2CB /* AppLockScreenViewModelProtocol.swift */; }; E82E13CC3EB923CCB8F8273C /* TimelineProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E543072DE58E751F028998 /* TimelineProxy.swift */; }; E84ADFE9696936C18C2424B5 /* SecureBackupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A00BB9CD12CF6AC98D5485 /* SecureBackupScreen.swift */; }; - E89536FC8C0E4B79E9842A78 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */; }; E8B290CBB7E5FF5E3C1B6124 /* KnockRequestsListEmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 627A8B5E798CC778C363655E /* KnockRequestsListEmptyStateView.swift */; }; E8C65C19F7C40EE545172DD6 /* RoomScreenFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */; }; E9347F56CF0683208F4D9249 /* RoomNotificationSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81A9B5225D0881CEFA2CF7C9 /* RoomNotificationSettingsScreenViewModel.swift */; }; @@ -1234,6 +1233,7 @@ F99FB21EFC6D99D247FE7CBE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DE8DC9B3FBA402117DC4C49F /* Kingfisher */; }; F9EA79092C18A8CFE4922DD2 /* PollFormScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F64A8582F65567AC38C2976A /* PollFormScreenViewModel.swift */; }; FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */; }; + FA53FA227FFBE469AFF32F71 /* TimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6C585CE1F721A2770C70D47 /* TimelineControllerProtocol.swift */; }; FA5A7E32B1920FCB4EEDC1BA /* RoomDetailsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6493AC9979CEB1410302BFE3 /* RoomDetailsScreenCoordinator.swift */; }; FA71CD334F2D2289BEF0D749 /* SecureBackupRecoveryKeyScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2FCA3D0F239B9E911B966B /* SecureBackupRecoveryKeyScreen.swift */; }; FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; }; @@ -1388,7 +1388,6 @@ 0833F51229E166BCA141D004 /* RoomRolesAndPermissionsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsFlowCoordinator.swift; sourceTree = ""; }; 086B997409328F091EBA43CE /* RoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenUITests.swift; sourceTree = ""; }; 086C19086DD16E9B38E25954 /* ReportContentViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentViewModelTests.swift; sourceTree = ""; }; - 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = ""; }; 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = ""; }; 0A3E77399BD262D301451BF2 /* RoomDetailsEditScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenCoordinator.swift; sourceTree = ""; }; 0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = ""; }; @@ -1452,7 +1451,6 @@ 18486B87745B1811E7FBD3D2 /* AnalyticsPromptScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenModels.swift; sourceTree = ""; }; 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecorationTimelineItemProtocol.swift; sourceTree = ""; }; 18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxy.swift; sourceTree = ""; }; - 18FE0CDF1FFA92EA7EE17B0B /* RoomTimelineControllerFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerFactoryProtocol.swift; sourceTree = ""; }; 190EC7285D3CFEF0D3011BCF /* GeoURI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoURI.swift; sourceTree = ""; }; 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenTrackerViewModifier.swift; sourceTree = ""; }; 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderProtocol.swift; sourceTree = ""; }; @@ -1470,6 +1468,7 @@ 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxyProtocol.swift; sourceTree = ""; }; 1B9D191A81FFB0C72CE73E77 /* RoomSelectionScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreenModels.swift; sourceTree = ""; }; 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = ""; }; + 1BA8082E26C77A2C587B34B3 /* MockTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTimelineController.swift; sourceTree = ""; }; 1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = ""; }; 1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = ""; }; 1C7F63EB1525E697CAEB002B /* BlankFormCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlankFormCoordinator.swift; sourceTree = ""; }; @@ -1563,7 +1562,6 @@ 2BB385E148DE55C85C0A02D6 /* SoftLogoutScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenModels.swift; sourceTree = ""; }; 2BDB3E65A79779EDA5D33D8A /* AudioPlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerState.swift; sourceTree = ""; }; 2BFDCA5A09EE70BC17F2EFA7 /* URLComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLComponents.swift; sourceTree = ""; }; - 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerProtocol.swift; sourceTree = ""; }; 2C39D91A31409775B0F4268F /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/InfoPlist.strings; sourceTree = ""; }; 2CEBCB9676FCD1D0F13188DD /* StringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringTests.swift; sourceTree = ""; }; 2CF9FE7E0CF9F40D1509E63A /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -1608,6 +1606,7 @@ 35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = ""; }; 36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = ""; }; + 371B248460BD1A3F20318137 /* TimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProvider.swift; sourceTree = ""; }; 376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = ""; }; 37A63A59BFDDC494B1C20119 /* CallScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallScreenViewModel.swift; sourceTree = ""; }; 37CA26F55123E36B50DB0B3A /* AttributedStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringTests.swift; sourceTree = ""; }; @@ -1748,7 +1747,6 @@ 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = ""; }; 5281C5CDC4A712265A0B5FBF /* PollRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollRoomTimelineItem.swift; sourceTree = ""; }; 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedString.swift; sourceTree = ""; }; - 52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineController.swift; sourceTree = ""; }; 52F5EE5DE3B55D59299DB5BC /* AppLockSetupBiometricsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupBiometricsScreenViewModelTests.swift; sourceTree = ""; }; 53482ECA4B6633961EC224F5 /* ScrollViewAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollViewAdapter.swift; sourceTree = ""; }; 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedRoomTimelineItem.swift; sourceTree = ""; }; @@ -1813,6 +1811,7 @@ 627A8B5E798CC778C363655E /* KnockRequestsListEmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListEmptyStateView.swift; sourceTree = ""; }; 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderTests.swift; sourceTree = ""; }; 62B07B296D7A9D2F09120853 /* OrderedSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderedSet.swift; sourceTree = ""; }; + 62EACAFB3F3E017060F9F1C5 /* TimelineProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProviderMock.swift; sourceTree = ""; }; 638790D3F915F0909315C47A /* PollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollView.swift; sourceTree = ""; }; 638A81B97D51591D0FCFA598 /* InteractiveQuickLook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveQuickLook.swift; sourceTree = ""; }; 63E8A1E8EE094F570573B6E8 /* RoomDetailsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -1832,7 +1831,6 @@ 669F35C505ACE1110589F875 /* MediaUploadingPreprocessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadingPreprocessor.swift; sourceTree = ""; }; 66AFD800AF033D8B0D11191A /* UserPropertiesExt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPropertiesExt.swift; sourceTree = ""; }; 66B96842BF5F8ACA1AC84C55 /* test_audio.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = test_audio.mp3; sourceTree = ""; }; - 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProvider.swift; sourceTree = ""; }; 66F91544AC136BF6477BDAB8 /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = ""; }; 671C338B7259DC5774816885 /* AuthenticationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceTests.swift; sourceTree = ""; }; 6722709BD6178E10B70C9641 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SAS.strings; sourceTree = ""; }; @@ -1907,6 +1905,7 @@ 7A03E073077D92AA19C43DCF /* IdentityConfirmationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenCoordinator.swift; sourceTree = ""; }; 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenViewModelProtocol.swift; sourceTree = ""; }; 7AC0CD1CAFD3F8B057F9AEA5 /* ClientBuilderHook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientBuilderHook.swift; sourceTree = ""; }; + 7AC1E3FE9B59EA094867863E /* TimelineControllerFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineControllerFactoryProtocol.swift; sourceTree = ""; }; 7AE094FCB6387D268C436161 /* SecureBackupScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenViewModel.swift; sourceTree = ""; }; 7AE75941583A033A9EDC9FE0 /* RoomChangePermissionsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModel.swift; sourceTree = ""; }; 7AFD012C3A9F5EF276DDD4AA /* AnalyticsPromptScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenViewModelProtocol.swift; sourceTree = ""; }; @@ -2001,6 +2000,7 @@ 8BCCE3D12B0A9C6E559B5B5A /* EmojiProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderProtocol.swift; sourceTree = ""; }; 8BD9C9A31D9AB3B6D8128E69 /* KnockRequestsListScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListScreenModels.swift; sourceTree = ""; }; 8BEBF0E59F25E842EDB6FD11 /* LocationSharingScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSharingScreenModels.swift; sourceTree = ""; }; + 8C0D5AE752AF87DA0A920812 /* TimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineControllerFactory.swift; sourceTree = ""; }; 8C44BBC892499BE45B074F89 /* AppLockScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenCoordinator.swift; sourceTree = ""; }; 8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItemContent.swift; sourceTree = ""; }; 8CC23C63849452BC86EA2852 /* ButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyle.swift; sourceTree = ""; }; @@ -2088,7 +2088,6 @@ 9ECF11669EF253E98AA2977A /* CompletionSuggestionServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionSuggestionServiceProtocol.swift; sourceTree = ""; }; 9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = ""; }; 9F40FB0A43DAECEC27C73722 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/SAS.strings; sourceTree = ""; }; - 9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineController.swift; sourceTree = ""; }; 9FD40B92FCF20165658296AD /* TimelineMediaPreviewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaPreviewModifier.swift; sourceTree = ""; }; A00C7A331B72C0F05C00392F /* RoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelProtocol.swift; sourceTree = ""; }; A010B8EAD1A9F6B4686DF2F4 /* BlockedUsersScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreenViewModel.swift; sourceTree = ""; }; @@ -2109,6 +2108,7 @@ A3FBD9C2B9A5479526920399 /* BugReportScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportScreenCoordinator.swift; sourceTree = ""; }; A40C19719687984FD9478FBE /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = ""; }; A40F1985065500F0E7F61A27 /* PollFormScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModelProtocol.swift; sourceTree = ""; }; + A4216C12C0369A8AB059EDE9 /* TimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineController.swift; sourceTree = ""; }; A433BE28B40D418237BE37B5 /* ReportContentScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreen.swift; sourceTree = ""; }; A436057DBEA1A23CA8CB1FD7 /* UIFont+AttributedStringBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIFont+AttributedStringBuilder.h"; sourceTree = ""; }; A443FAE2EE820A5790C35C8D /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = ""; }; @@ -2174,6 +2174,7 @@ B0618820D26F9871A4BBB40E /* ComposerToolbarViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbarViewModelProtocol.swift; sourceTree = ""; }; B0A307A44F952CD73E63AE31 /* RoomEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomEventStringBuilder.swift; sourceTree = ""; }; B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSettingsScreenViewModelTests.swift; sourceTree = ""; }; + B0F5CC38803B8382D2C63222 /* TimelineControllerFactoryMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineControllerFactoryMock.swift; sourceTree = ""; }; B14B1DE3E2D5D26732C49036 /* RoomChangeRolesScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenViewModel.swift; sourceTree = ""; }; B16048D30F0438731C41F775 /* StateRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineItem.swift; sourceTree = ""; }; B172057567E049007A5C4D92 /* Strings+SAS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Strings+SAS.swift"; sourceTree = ""; }; @@ -2204,6 +2205,7 @@ B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = ""; }; B655A536341D2695158C6664 /* AuthenticationClientBuilderFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClientBuilderFactory.swift; sourceTree = ""; }; B6A293D06BAB2B7A17D9314B /* VoiceMessageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineView.swift; sourceTree = ""; }; + B6C585CE1F721A2770C70D47 /* TimelineControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineControllerProtocol.swift; sourceTree = ""; }; B6E4AB573FAEBB7B853DD04C /* AppHooks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppHooks.swift; sourceTree = ""; }; B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; B73587C2E3CF5998361AE516 /* HomeScreenRoomTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomTests.swift; sourceTree = ""; }; @@ -2345,7 +2347,6 @@ D3F219838588C62198E726E3 /* LABiometryType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LABiometryType.swift; sourceTree = ""; }; D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreen.swift; sourceTree = ""; }; D45C9EAA86423D7D3126DE4F /* VoiceMessageRecorderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorderProtocol.swift; sourceTree = ""; }; - D479DF730528153665E5782E /* RoomTimelineControllerFactoryMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerFactoryMock.swift; sourceTree = ""; }; D49B9785E3AD7D1C15A29F2F /* MediaSourceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaSourceProxy.swift; sourceTree = ""; }; D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; D529B976F8B2AA654D923422 /* VoiceMessageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomTimelineItem.swift; sourceTree = ""; }; @@ -2383,6 +2384,7 @@ DCA2D836BD10303F37FAAEED /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = ""; }; DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionModels.swift; sourceTree = ""; }; DCF239C619971FDE48132550 /* SecureBackupLogoutConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenModels.swift; sourceTree = ""; }; + DD8C9BBB729C941BEE0E2A63 /* TimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProviderProtocol.swift; sourceTree = ""; }; DD955A0380C287C418F1A74D /* PhotoLibraryManagerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryManagerMock.swift; sourceTree = ""; }; DD97F9661ABF08CE002054A2 /* AppLockServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceTests.swift; sourceTree = ""; }; DE5127D6EA05B2E45D0A7D59 /* JoinRoomScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModelTests.swift; sourceTree = ""; }; @@ -2430,7 +2432,6 @@ E6935A55AB3B0C94BC566DD6 /* EncryptionResetPasswordScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreenCoordinator.swift; sourceTree = ""; }; E6E6BDF9D26DB05C88901416 /* RedactedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineItem.swift; sourceTree = ""; }; E6F5D66F158A6662F953733E /* NotificationSettingsProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsProxy.swift; sourceTree = ""; }; - E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerFactory.swift; sourceTree = ""; }; E7495E1119753B06FF2C2279 /* PhotoLibraryManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoLibraryManager.swift; sourceTree = ""; }; E76A706B3EEA32B882DA5E2D /* BlockedUsersScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreenViewModelProtocol.swift; sourceTree = ""; }; E78FC546F28E045A560F2963 /* EncryptionKeyProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionKeyProviderProtocol.swift; sourceTree = ""; }; @@ -2509,7 +2510,6 @@ F64A8582F65567AC38C2976A /* PollFormScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModel.swift; sourceTree = ""; }; F72EFC8C634469F9262659C7 /* NSItemProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSItemProvider.swift; sourceTree = ""; }; F733F135E6D67BBBEB76CC30 /* AppLockUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockUITests.swift; sourceTree = ""; }; - F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderMock.swift; sourceTree = ""; }; F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreen.swift; sourceTree = ""; }; F7C161B06F417CA5D1F1E088 /* WebRegistrationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRegistrationScreenModels.swift; sourceTree = ""; }; F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinatorTests.swift; sourceTree = ""; }; @@ -3137,11 +3137,11 @@ 2F2FED77226A43559F009463 /* TimelineController */ = { isa = PBXGroup; children = ( - 52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */, - 9F85164F9475FF2867F71AAA /* RoomTimelineController.swift */, - E6FCC416A3BFE73DF7B3E6BF /* RoomTimelineControllerFactory.swift */, - 18FE0CDF1FFA92EA7EE17B0B /* RoomTimelineControllerFactoryProtocol.swift */, - 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */, + 1BA8082E26C77A2C587B34B3 /* MockTimelineController.swift */, + A4216C12C0369A8AB059EDE9 /* TimelineController.swift */, + 8C0D5AE752AF87DA0A920812 /* TimelineControllerFactory.swift */, + 7AC1E3FE9B59EA094867863E /* TimelineControllerFactoryProtocol.swift */, + B6C585CE1F721A2770C70D47 /* TimelineControllerProtocol.swift */, ); path = TimelineController; sourceTree = ""; @@ -3205,10 +3205,10 @@ F5D1BAA90F3A073D91B4F16B /* RoomNotificationSettingsProxyMock.swift */, 6695C64F066628411EAD21E9 /* RoomPreviewProxyMock.swift */, FC83F47D2173B7538AA72E0E /* RoomSummaryProviderMock.swift */, - D479DF730528153665E5782E /* RoomTimelineControllerFactoryMock.swift */, - F74532E01B317C56C1BE8FA8 /* RoomTimelineProviderMock.swift */, 4AB29A2D95D3469B5F016655 /* SecureBackupControllerMock.swift */, 248649EBA5BC33DB93698734 /* SessionVerificationControllerProxyMock.swift */, + B0F5CC38803B8382D2C63222 /* TimelineControllerFactoryMock.swift */, + 62EACAFB3F3E017060F9F1C5 /* TimelineProviderMock.swift */, 17A8AA0DFA06012A9DAB951E /* TimelineProxyMock.swift */, 7893780A1FD6E3F38B3E9049 /* UserIndicatorControllerMock.swift */, AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */, @@ -5936,11 +5936,11 @@ children = ( 190EC7285D3CFEF0D3011BCF /* GeoURI.swift */, 0DF5CBAF69BDF5DF31C661E1 /* IntentionalMentions.swift */, - 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */, - 095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */, E48C91C8BE55CAE1A3DBC3BC /* TimelineItemIdentifier.swift */, 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */, 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */, + 371B248460BD1A3F20318137 /* TimelineProvider.swift */, + DD8C9BBB729C941BEE0E2A63 /* TimelineProviderProtocol.swift */, F9E543072DE58E751F028998 /* TimelineProxy.swift */, B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */, 3EA31CC7012EA2A5653DAFC9 /* Fixtures */, @@ -7192,8 +7192,8 @@ F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */, C13128AAA787A4C2CBE4EE82 /* MessageForwardingScreenViewModelProtocol.swift in Sources */, C97325EFDCCEE457432A9E82 /* MessageText.swift in Sources */, - 09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */, AF2ABA2794E376B64104C964 /* MockSoftLogoutScreenState.swift in Sources */, + 530C2238E40F71223327FC95 /* MockTimelineController.swift in Sources */, F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */, EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */, FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */, @@ -7419,11 +7419,6 @@ 983896D611ABF52A5C37498D /* RoomSummaryProvider.swift in Sources */, B5899F18AD6C56CE08FE532B /* RoomSummaryProviderMock.swift in Sources */, AA050DF4AEE54A641BA7CA22 /* RoomSummaryProviderProtocol.swift in Sources */, - 2ABF11717C64054CEF2819A3 /* RoomTimelineController.swift in Sources */, - 38896D54D6D675534E606195 /* RoomTimelineControllerFactory.swift in Sources */, - B31E5493C99381D4E204438B /* RoomTimelineControllerFactoryMock.swift in Sources */, - 7ECF12D5DCD69F67BD3E3842 /* RoomTimelineControllerFactoryProtocol.swift in Sources */, - E89536FC8C0E4B79E9842A78 /* RoomTimelineControllerProtocol.swift in Sources */, 4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */, 13C77FDF17C4C6627CFFC205 /* RoomTimelineItemFactoryProtocol.swift in Sources */, 70558528EF68CAAEF09972D5 /* RoomTimelineItemFixtures.swift in Sources */, @@ -7431,9 +7426,6 @@ 1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */, AD55E245FE686D7DB4C86406 /* RoomTimelineItemView.swift in Sources */, 41CE5E1289C8768FC5B6490C /* RoomTimelineItemViewState.swift in Sources */, - 9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */, - B272E5D1DE8BDA87A6B7A696 /* RoomTimelineProviderMock.swift in Sources */, - 77D7DAA41AAB36800C1F2E2D /* RoomTimelineProviderProtocol.swift in Sources */, B2F8E01ABA1BA30265B4ECBE /* RoundedCornerShape.swift in Sources */, D43F0503EF2CBC55272538FE /* SDKGeneratedMocks.swift in Sources */, 88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */, @@ -7548,6 +7540,11 @@ D8CFA0EE46376F9FF04EEE45 /* TextRoomTimelineView.swift in Sources */, 71643093F87153F633A1B025 /* ThreadDecorator.swift in Sources */, 2AED12987603157C32C2114D /* TimelineBubbleLayout.swift in Sources */, + 2BFA4C6D5B3D327B02C66AB0 /* TimelineController.swift in Sources */, + 7DCFC31B49BDD2BC32184E58 /* TimelineControllerFactory.swift in Sources */, + 63FD7DBE7BBA149B4B8B99D7 /* TimelineControllerFactoryMock.swift in Sources */, + B47213F07A67CE3A8D49CEC9 /* TimelineControllerFactoryProtocol.swift in Sources */, + FA53FA227FFBE469AFF32F71 /* TimelineControllerProtocol.swift in Sources */, 798BF3072137833FBD3F4C96 /* TimelineDeliveryStatusView.swift in Sources */, 109AEB7D33C4497727AFB87F /* TimelineInteractionHandler.swift in Sources */, E6FA87F773424B27614B23E9 /* TimelineItemAccessibilityModifier.swift in Sources */, @@ -7574,6 +7571,9 @@ A32384E3D85CA65342D3A908 /* TimelineMediaPreviewRedactConfirmationView.swift in Sources */, 86769B62BAE17601B3AE1B60 /* TimelineMediaPreviewViewModel.swift in Sources */, B818580464CFB5400A3EF6AE /* TimelineModels.swift in Sources */, + DA03B4F28C4D248EECE3429F /* TimelineProvider.swift in Sources */, + 4E4EF97B9F9CEFAC726BA72F /* TimelineProviderMock.swift in Sources */, + 5EC046E41755C095DAB1C3FF /* TimelineProviderProtocol.swift in Sources */, E82E13CC3EB923CCB8F8273C /* TimelineProxy.swift in Sources */, 16A1F6C703305FCAF4E14EC6 /* TimelineProxyMock.swift in Sources */, 2FEC6652055984389CE1BBEC /* TimelineProxyProtocol.swift in Sources */, diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index be07ab405..1336e63fd 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -534,7 +534,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg appLockService: appLockFlowCoordinator.appLockService, bugReportService: ServiceLocator.shared.bugReportService, elementCallService: elementCallService, - roomTimelineControllerFactory: RoomTimelineControllerFactory(), + timelineControllerFactory: TimelineControllerFactory(), appMediator: appMediator, appSettings: appSettings, appHooks: appHooks, diff --git a/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift index 656f39d3a..6fcdc6465 100644 --- a/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/MediaEventsTimelineFlowCoordinator.swift @@ -16,7 +16,7 @@ enum MediaEventsTimelineFlowCoordinatorAction { class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { private let navigationStackCoordinator: NavigationStackCoordinator private let userSession: UserSessionProtocol - private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol + private let timelineControllerFactory: TimelineControllerFactoryProtocol private let roomProxy: JoinedRoomProxyProtocol private let userIndicatorController: UserIndicatorControllerProtocol private let appMediator: AppMediatorProtocol @@ -31,14 +31,14 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { init(navigationStackCoordinator: NavigationStackCoordinator, userSession: UserSessionProtocol, - roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, + timelineControllerFactory: TimelineControllerFactoryProtocol, roomProxy: JoinedRoomProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol, appMediator: AppMediatorProtocol, emojiProvider: EmojiProviderProtocol) { self.navigationStackCoordinator = navigationStackCoordinator self.userSession = userSession - self.roomTimelineControllerFactory = roomTimelineControllerFactory + self.timelineControllerFactory = timelineControllerFactory self.roomProxy = roomProxy self.userIndicatorController = userIndicatorController self.appMediator = appMediator @@ -64,18 +64,20 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), stateEventStringBuilder: RoomStateEventStringBuilder(userID: userSession.clientProxy.userID)) - guard case let .success(mediaTimelineController) = await roomTimelineControllerFactory.buildMessageFilteredRoomTimelineController(allowedMessageTypes: [.image, .video], - roomProxy: roomProxy, - timelineItemFactory: timelineItemFactory, - mediaProvider: userSession.mediaProvider) else { + guard case let .success(mediaTimelineController) = await timelineControllerFactory.buildMessageFilteredTimelineController(allowedMessageTypes: [.image, .video], + presentation: .mediaFilesScreen, + roomProxy: roomProxy, + timelineItemFactory: timelineItemFactory, + mediaProvider: userSession.mediaProvider) else { MXLog.error("Failed presenting media timeline") return } - guard case let .success(filesTimelineController) = await roomTimelineControllerFactory.buildMessageFilteredRoomTimelineController(allowedMessageTypes: [.file, .audio], - roomProxy: roomProxy, - timelineItemFactory: timelineItemFactory, - mediaProvider: userSession.mediaProvider) else { + guard case let .success(filesTimelineController) = await timelineControllerFactory.buildMessageFilteredTimelineController(allowedMessageTypes: [.file, .audio], + presentation: .mediaFilesScreen, + roomProxy: roomProxy, + timelineItemFactory: timelineItemFactory, + mediaProvider: userSession.mediaProvider) else { MXLog.error("Failed presenting media timeline") return } @@ -88,7 +90,8 @@ class MediaEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { voiceMessageMediaManager: userSession.voiceMessageMediaManager, appMediator: appMediator, emojiProvider: emojiProvider, - userIndicatorController: userIndicatorController) + userIndicatorController: userIndicatorController, + timelineControllerFactory: timelineControllerFactory) let coordinator = MediaEventsTimelineScreenCoordinator(parameters: parameters) diff --git a/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift index a2175b4f6..608b77e92 100644 --- a/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/PinnedEventsTimelineFlowCoordinator.swift @@ -18,7 +18,7 @@ enum PinnedEventsTimelineFlowCoordinatorAction { class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { private let navigationStackCoordinator: NavigationStackCoordinator private let userSession: UserSessionProtocol - private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol + private let timelineControllerFactory: TimelineControllerFactoryProtocol private let roomProxy: JoinedRoomProxyProtocol private let userIndicatorController: UserIndicatorControllerProtocol private let appMediator: AppMediatorProtocol @@ -33,14 +33,14 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { init(navigationStackCoordinator: NavigationStackCoordinator, userSession: UserSessionProtocol, - roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, + timelineControllerFactory: TimelineControllerFactoryProtocol, roomProxy: JoinedRoomProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol, appMediator: AppMediatorProtocol, emojiProvider: EmojiProviderProtocol) { self.navigationStackCoordinator = navigationStackCoordinator self.userSession = userSession - self.roomTimelineControllerFactory = roomTimelineControllerFactory + self.timelineControllerFactory = timelineControllerFactory self.roomProxy = roomProxy self.userIndicatorController = userIndicatorController self.appMediator = appMediator @@ -65,9 +65,9 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) - guard let timelineController = await roomTimelineControllerFactory.buildPinnedEventsRoomTimelineController(roomProxy: roomProxy, - timelineItemFactory: timelineItemFactory, - mediaProvider: userSession.mediaProvider) else { + guard let timelineController = await timelineControllerFactory.buildPinnedEventsTimelineController(roomProxy: roomProxy, + timelineItemFactory: timelineItemFactory, + mediaProvider: userSession.mediaProvider) else { fatalError("This can never fail because we allow this view to be presented only when the timeline is fully loaded and not nil") } @@ -77,7 +77,8 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol { mediaPlayerProvider: MediaPlayerProvider(), voiceMessageMediaManager: userSession.voiceMessageMediaManager, appMediator: appMediator, - emojiProvider: emojiProvider)) + emojiProvider: emojiProvider, + timelineControllerFactory: timelineControllerFactory)) coordinator.actions .sink { [weak self] action in diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index e375b494d..d7959de42 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -78,7 +78,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { private let roomID: String private let userSession: UserSessionProtocol private let isChildFlow: Bool - private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol + private let timelineControllerFactory: TimelineControllerFactoryProtocol private let navigationStackCoordinator: NavigationStackCoordinator private let emojiProvider: EmojiProviderProtocol private let ongoingCallRoomIDPublisher: CurrentValuePublisher @@ -110,12 +110,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { actionsSubject.eraseToAnyPublisher() } - private var timelineController: RoomTimelineControllerProtocol? + private var timelineController: TimelineControllerProtocol? init(roomID: String, userSession: UserSessionProtocol, isChildFlow: Bool, - roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, + timelineControllerFactory: TimelineControllerFactoryProtocol, navigationStackCoordinator: NavigationStackCoordinator, emojiProvider: EmojiProviderProtocol, ongoingCallRoomIDPublisher: CurrentValuePublisher, @@ -126,7 +126,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { self.roomID = roomID self.userSession = userSession self.isChildFlow = isChildFlow - self.roomTimelineControllerFactory = roomTimelineControllerFactory + self.timelineControllerFactory = timelineControllerFactory self.navigationStackCoordinator = navigationStackCoordinator self.emojiProvider = emojiProvider self.ongoingCallRoomIDPublisher = ongoingCallRoomIDPublisher @@ -685,10 +685,10 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { let timelineItemFactory = RoomTimelineItemFactory(userID: userID, attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) - let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy, - initialFocussedEventID: presentationAction?.focusedEvent?.eventID, - timelineItemFactory: timelineItemFactory, - mediaProvider: userSession.mediaProvider) + let timelineController = timelineControllerFactory.buildTimelineController(roomProxy: roomProxy, + initialFocussedEventID: presentationAction?.focusedEvent?.eventID, + timelineItemFactory: timelineItemFactory, + mediaProvider: userSession.mediaProvider) self.timelineController = timelineController let completionSuggestionService = CompletionSuggestionService(roomProxy: roomProxy) @@ -707,7 +707,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { ongoingCallRoomIDPublisher: ongoingCallRoomIDPublisher, appMediator: appMediator, appSettings: appSettings, - composerDraftService: composerDraftService) + composerDraftService: composerDraftService, + timelineControllerFactory: timelineControllerFactory) let coordinator = RoomScreenCoordinator(parameters: parameters) coordinator.actions @@ -1202,13 +1203,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) - let roomTimelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy, - initialFocussedEventID: nil, - timelineItemFactory: timelineItemFactory, - mediaProvider: userSession.mediaProvider) + let timelineController = timelineControllerFactory.buildTimelineController(roomProxy: roomProxy, + initialFocussedEventID: nil, + timelineItemFactory: timelineItemFactory, + mediaProvider: userSession.mediaProvider) let parameters = RoomPollsHistoryScreenCoordinatorParameters(pollInteractionHandler: PollInteractionHandler(analyticsService: analytics, roomProxy: roomProxy), - roomTimelineController: roomTimelineController) + timelineController: timelineController) let coordinator = RoomPollsHistoryScreenCoordinator(parameters: parameters) coordinator.actions .sink { [weak self] action in @@ -1513,7 +1514,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { let coordinator = await RoomFlowCoordinator(roomID: roomID, userSession: userSession, isChildFlow: true, - roomTimelineControllerFactory: roomTimelineControllerFactory, + timelineControllerFactory: timelineControllerFactory, navigationStackCoordinator: navigationStackCoordinator, emojiProvider: emojiProvider, ongoingCallRoomIDPublisher: ongoingCallRoomIDPublisher, @@ -1551,7 +1552,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { let flowCoordinator = PinnedEventsTimelineFlowCoordinator(navigationStackCoordinator: stackCoordinator, userSession: userSession, - roomTimelineControllerFactory: roomTimelineControllerFactory, + timelineControllerFactory: timelineControllerFactory, roomProxy: roomProxy, userIndicatorController: userIndicatorController, appMediator: appMediator, @@ -1590,7 +1591,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { private func startMediaEventsTimelineFlow() async { let flowCoordinator = MediaEventsTimelineFlowCoordinator(navigationStackCoordinator: navigationStackCoordinator, userSession: userSession, - roomTimelineControllerFactory: roomTimelineControllerFactory, + timelineControllerFactory: timelineControllerFactory, roomProxy: roomProxy, userIndicatorController: userIndicatorController, appMediator: appMediator, diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 8356e0679..e76727caf 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -34,7 +34,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { // periphery:ignore - retaining purpose private var roomFlowCoordinator: RoomFlowCoordinator? - private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol + private let timelineControllerFactory: TimelineControllerFactoryProtocol private let settingsFlowCoordinator: SettingsFlowCoordinator @@ -69,7 +69,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { appLockService: AppLockServiceProtocol, bugReportService: BugReportServiceProtocol, elementCallService: ElementCallServiceProtocol, - roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, + timelineControllerFactory: TimelineControllerFactoryProtocol, appMediator: AppMediatorProtocol, appSettings: AppSettings, appHooks: AppHooks, @@ -81,7 +81,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { self.navigationRootCoordinator = navigationRootCoordinator self.bugReportService = bugReportService self.elementCallService = elementCallService - self.roomTimelineControllerFactory = roomTimelineControllerFactory + self.timelineControllerFactory = timelineControllerFactory self.appMediator = appMediator self.appSettings = appSettings self.appHooks = appHooks @@ -574,7 +574,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { let coordinator = await RoomFlowCoordinator(roomID: roomID, userSession: userSession, isChildFlow: false, - roomTimelineControllerFactory: roomTimelineControllerFactory, + timelineControllerFactory: timelineControllerFactory, navigationStackCoordinator: detailNavigationStackCoordinator, emojiProvider: EmojiProvider(appSettings: appSettings), ongoingCallRoomIDPublisher: elementCallService.ongoingCallRoomIDPublisher, diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index 8a5d62be6..7274e1f89 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -6228,15 +6228,15 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable { } //MARK: - messageFilteredTimeline - var messageFilteredTimelineAllowedMessageTypesUnderlyingCallsCount = 0 - var messageFilteredTimelineAllowedMessageTypesCallsCount: Int { + var messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingCallsCount = 0 + var messageFilteredTimelineAllowedMessageTypesPresentationCallsCount: Int { get { if Thread.isMainThread { - return messageFilteredTimelineAllowedMessageTypesUnderlyingCallsCount + return messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = messageFilteredTimelineAllowedMessageTypesUnderlyingCallsCount + returnValue = messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingCallsCount } return returnValue! @@ -6244,29 +6244,29 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable { } set { if Thread.isMainThread { - messageFilteredTimelineAllowedMessageTypesUnderlyingCallsCount = newValue + messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - messageFilteredTimelineAllowedMessageTypesUnderlyingCallsCount = newValue + messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingCallsCount = newValue } } } } - var messageFilteredTimelineAllowedMessageTypesCalled: Bool { - return messageFilteredTimelineAllowedMessageTypesCallsCount > 0 + var messageFilteredTimelineAllowedMessageTypesPresentationCalled: Bool { + return messageFilteredTimelineAllowedMessageTypesPresentationCallsCount > 0 } - var messageFilteredTimelineAllowedMessageTypesReceivedAllowedMessageTypes: [RoomMessageEventMessageType]? - var messageFilteredTimelineAllowedMessageTypesReceivedInvocations: [[RoomMessageEventMessageType]] = [] + var messageFilteredTimelineAllowedMessageTypesPresentationReceivedArguments: (allowedMessageTypes: [RoomMessageEventMessageType], presentation: TimelineKind.MediaPresentation)? + var messageFilteredTimelineAllowedMessageTypesPresentationReceivedInvocations: [(allowedMessageTypes: [RoomMessageEventMessageType], presentation: TimelineKind.MediaPresentation)] = [] - var messageFilteredTimelineAllowedMessageTypesUnderlyingReturnValue: Result! - var messageFilteredTimelineAllowedMessageTypesReturnValue: Result! { + var messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingReturnValue: Result! + var messageFilteredTimelineAllowedMessageTypesPresentationReturnValue: Result! { get { if Thread.isMainThread { - return messageFilteredTimelineAllowedMessageTypesUnderlyingReturnValue + return messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingReturnValue } else { var returnValue: Result? = nil DispatchQueue.main.sync { - returnValue = messageFilteredTimelineAllowedMessageTypesUnderlyingReturnValue + returnValue = messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingReturnValue } return returnValue! @@ -6274,26 +6274,26 @@ class JoinedRoomProxyMock: JoinedRoomProxyProtocol, @unchecked Sendable { } set { if Thread.isMainThread { - messageFilteredTimelineAllowedMessageTypesUnderlyingReturnValue = newValue + messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - messageFilteredTimelineAllowedMessageTypesUnderlyingReturnValue = newValue + messageFilteredTimelineAllowedMessageTypesPresentationUnderlyingReturnValue = newValue } } } } - var messageFilteredTimelineAllowedMessageTypesClosure: (([RoomMessageEventMessageType]) async -> Result)? + var messageFilteredTimelineAllowedMessageTypesPresentationClosure: (([RoomMessageEventMessageType], TimelineKind.MediaPresentation) async -> Result)? - func messageFilteredTimeline(allowedMessageTypes: [RoomMessageEventMessageType]) async -> Result { - messageFilteredTimelineAllowedMessageTypesCallsCount += 1 - messageFilteredTimelineAllowedMessageTypesReceivedAllowedMessageTypes = allowedMessageTypes + func messageFilteredTimeline(allowedMessageTypes: [RoomMessageEventMessageType], presentation: TimelineKind.MediaPresentation) async -> Result { + messageFilteredTimelineAllowedMessageTypesPresentationCallsCount += 1 + messageFilteredTimelineAllowedMessageTypesPresentationReceivedArguments = (allowedMessageTypes: allowedMessageTypes, presentation: presentation) DispatchQueue.main.async { - self.messageFilteredTimelineAllowedMessageTypesReceivedInvocations.append(allowedMessageTypes) + self.messageFilteredTimelineAllowedMessageTypesPresentationReceivedInvocations.append((allowedMessageTypes: allowedMessageTypes, presentation: presentation)) } - if let messageFilteredTimelineAllowedMessageTypesClosure = messageFilteredTimelineAllowedMessageTypesClosure { - return await messageFilteredTimelineAllowedMessageTypesClosure(allowedMessageTypes) + if let messageFilteredTimelineAllowedMessageTypesPresentationClosure = messageFilteredTimelineAllowedMessageTypesPresentationClosure { + return await messageFilteredTimelineAllowedMessageTypesPresentationClosure(allowedMessageTypes, presentation) } else { - return messageFilteredTimelineAllowedMessageTypesReturnValue + return messageFilteredTimelineAllowedMessageTypesPresentationReturnValue } } //MARK: - enableEncryption @@ -13613,243 +13613,6 @@ class RoomSummaryProviderMock: RoomSummaryProviderProtocol, @unchecked Sendable setFilterClosure?(filter) } } -class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol, @unchecked Sendable { - - //MARK: - buildRoomTimelineController - - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0 - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount: Int { - get { - if Thread.isMainThread { - return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue - } - } - } - } - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCalled: Bool { - return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount > 0 - } - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)? - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = [] - - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue: RoomTimelineControllerProtocol! - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue: RoomTimelineControllerProtocol! { - get { - if Thread.isMainThread { - return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue - } else { - var returnValue: RoomTimelineControllerProtocol? = nil - DispatchQueue.main.sync { - returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue - } - } - } - } - var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure: ((JoinedRoomProxyProtocol, String?, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) -> RoomTimelineControllerProtocol)? - - func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol { - buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount += 1 - buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider) - DispatchQueue.main.async { - self.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)) - } - if let buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure { - return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure(roomProxy, initialFocussedEventID, timelineItemFactory, mediaProvider) - } else { - return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue - } - } - //MARK: - buildPinnedEventsRoomTimelineController - - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0 - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount: Int { - get { - if Thread.isMainThread { - return buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue - } - } - } - } - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCalled: Bool { - return buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount > 0 - } - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)? - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = [] - - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue: RoomTimelineControllerProtocol? - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue: RoomTimelineControllerProtocol? { - get { - if Thread.isMainThread { - return buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue - } else { - var returnValue: RoomTimelineControllerProtocol?? = nil - DispatchQueue.main.sync { - returnValue = buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue - } - } - } - } - var buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure: ((JoinedRoomProxyProtocol, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) async -> RoomTimelineControllerProtocol?)? - - func buildPinnedEventsRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? { - buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount += 1 - buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider) - DispatchQueue.main.async { - self.buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)) - } - if let buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure = buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure { - return await buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure(roomProxy, timelineItemFactory, mediaProvider) - } else { - return buildPinnedEventsRoomTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue - } - } - //MARK: - buildMessageFilteredRoomTimelineController - - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0 - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderCallsCount: Int { - get { - if Thread.isMainThread { - return buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue - } - } - } - } - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderCalled: Bool { - return buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderCallsCount > 0 - } - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderReceivedArguments: (allowedMessageTypes: [RoomMessageEventMessageType], roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)? - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations: [(allowedMessageTypes: [RoomMessageEventMessageType], roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = [] - - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue: Result! - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderReturnValue: Result! { - get { - if Thread.isMainThread { - return buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue - } else { - var returnValue: Result? = nil - DispatchQueue.main.sync { - returnValue = buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue - } else { - DispatchQueue.main.sync { - buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue - } - } - } - } - var buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderClosure: (([RoomMessageEventMessageType], JoinedRoomProxyProtocol, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) async -> Result)? - - func buildMessageFilteredRoomTimelineController(allowedMessageTypes: [RoomMessageEventMessageType], roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) async -> Result { - buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderCallsCount += 1 - buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderReceivedArguments = (allowedMessageTypes: allowedMessageTypes, roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider) - DispatchQueue.main.async { - self.buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations.append((allowedMessageTypes: allowedMessageTypes, roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)) - } - if let buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderClosure = buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderClosure { - return await buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderClosure(allowedMessageTypes, roomProxy, timelineItemFactory, mediaProvider) - } else { - return buildMessageFilteredRoomTimelineControllerAllowedMessageTypesRoomProxyTimelineItemFactoryMediaProviderReturnValue - } - } -} -class RoomTimelineProviderMock: RoomTimelineProviderProtocol, @unchecked Sendable { - var updatePublisher: AnyPublisher<([TimelineItemProxy], PaginationState), Never> { - get { return underlyingUpdatePublisher } - set(value) { underlyingUpdatePublisher = value } - } - var underlyingUpdatePublisher: AnyPublisher<([TimelineItemProxy], PaginationState), Never>! - var itemProxies: [TimelineItemProxy] = [] - var paginationState: PaginationState { - get { return underlyingPaginationState } - set(value) { underlyingPaginationState = value } - } - var underlyingPaginationState: PaginationState! - var kind: TimelineKind { - get { return underlyingKind } - set(value) { underlyingKind = value } - } - var underlyingKind: TimelineKind! - var membershipChangePublisher: AnyPublisher { - get { return underlyingMembershipChangePublisher } - set(value) { underlyingMembershipChangePublisher = value } - } - var underlyingMembershipChangePublisher: AnyPublisher! - -} class SecureBackupControllerMock: SecureBackupControllerProtocol, @unchecked Sendable { var recoveryState: CurrentValuePublisher { get { return underlyingRecoveryState } @@ -14651,12 +14414,249 @@ class SessionVerificationControllerProxyMock: SessionVerificationControllerProxy } } } +class TimelineControllerFactoryMock: TimelineControllerFactoryProtocol, @unchecked Sendable { + + //MARK: - buildTimelineController + + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0 + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount: Int { + get { + if Thread.isMainThread { + return buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue + } + } + } + } + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCalled: Bool { + return buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount > 0 + } + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)? + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = [] + + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue: TimelineControllerProtocol! + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue: TimelineControllerProtocol! { + get { + if Thread.isMainThread { + return buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue + } else { + var returnValue: TimelineControllerProtocol? = nil + DispatchQueue.main.sync { + returnValue = buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue + } + } + } + } + var buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure: ((JoinedRoomProxyProtocol, String?, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) -> TimelineControllerProtocol)? + + func buildTimelineController(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) -> TimelineControllerProtocol { + buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount += 1 + buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider) + DispatchQueue.main.async { + self.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)) + } + if let buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure = buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure { + return buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure(roomProxy, initialFocussedEventID, timelineItemFactory, mediaProvider) + } else { + return buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue + } + } + //MARK: - buildPinnedEventsTimelineController + + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0 + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount: Int { + get { + if Thread.isMainThread { + return buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue + } + } + } + } + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCalled: Bool { + return buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount > 0 + } + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)? + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = [] + + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue: TimelineControllerProtocol? + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue: TimelineControllerProtocol? { + get { + if Thread.isMainThread { + return buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue + } else { + var returnValue: TimelineControllerProtocol?? = nil + DispatchQueue.main.sync { + returnValue = buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue + } + } + } + } + var buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure: ((JoinedRoomProxyProtocol, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) async -> TimelineControllerProtocol?)? + + func buildPinnedEventsTimelineController(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) async -> TimelineControllerProtocol? { + buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount += 1 + buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider) + DispatchQueue.main.async { + self.buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)) + } + if let buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure = buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure { + return await buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure(roomProxy, timelineItemFactory, mediaProvider) + } else { + return buildPinnedEventsTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue + } + } + //MARK: - buildMessageFilteredTimelineController + + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0 + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderCallsCount: Int { + get { + if Thread.isMainThread { + return buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount + } else { + var returnValue: Int? = nil + DispatchQueue.main.sync { + returnValue = buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue + } else { + DispatchQueue.main.sync { + buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue + } + } + } + } + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderCalled: Bool { + return buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderCallsCount > 0 + } + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderReceivedArguments: (allowedMessageTypes: [RoomMessageEventMessageType], presentation: TimelineKind.MediaPresentation, roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)? + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations: [(allowedMessageTypes: [RoomMessageEventMessageType], presentation: TimelineKind.MediaPresentation, roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = [] + + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue: Result! + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderReturnValue: Result! { + get { + if Thread.isMainThread { + return buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue + } else { + var returnValue: Result? = nil + DispatchQueue.main.sync { + returnValue = buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue + } + + return returnValue! + } + } + set { + if Thread.isMainThread { + buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue + } else { + DispatchQueue.main.sync { + buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue + } + } + } + } + var buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderClosure: (([RoomMessageEventMessageType], TimelineKind.MediaPresentation, JoinedRoomProxyProtocol, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) async -> Result)? + + func buildMessageFilteredTimelineController(allowedMessageTypes: [RoomMessageEventMessageType], presentation: TimelineKind.MediaPresentation, roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) async -> Result { + buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderCallsCount += 1 + buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderReceivedArguments = (allowedMessageTypes: allowedMessageTypes, presentation: presentation, roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider) + DispatchQueue.main.async { + self.buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations.append((allowedMessageTypes: allowedMessageTypes, presentation: presentation, roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)) + } + if let buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderClosure = buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderClosure { + return await buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderClosure(allowedMessageTypes, presentation, roomProxy, timelineItemFactory, mediaProvider) + } else { + return buildMessageFilteredTimelineControllerAllowedMessageTypesPresentationRoomProxyTimelineItemFactoryMediaProviderReturnValue + } + } +} +class TimelineProviderMock: TimelineProviderProtocol, @unchecked Sendable { + var updatePublisher: AnyPublisher<([TimelineItemProxy], PaginationState), Never> { + get { return underlyingUpdatePublisher } + set(value) { underlyingUpdatePublisher = value } + } + var underlyingUpdatePublisher: AnyPublisher<([TimelineItemProxy], PaginationState), Never>! + var itemProxies: [TimelineItemProxy] = [] + var paginationState: PaginationState { + get { return underlyingPaginationState } + set(value) { underlyingPaginationState = value } + } + var underlyingPaginationState: PaginationState! + var kind: TimelineKind { + get { return underlyingKind } + set(value) { underlyingKind = value } + } + var underlyingKind: TimelineKind! + var membershipChangePublisher: AnyPublisher { + get { return underlyingMembershipChangePublisher } + set(value) { underlyingMembershipChangePublisher = value } + } + var underlyingMembershipChangePublisher: AnyPublisher! + +} class TimelineProxyMock: TimelineProxyProtocol, @unchecked Sendable { - var timelineProvider: RoomTimelineProviderProtocol { + var timelineProvider: TimelineProviderProtocol { get { return underlyingTimelineProvider } set(value) { underlyingTimelineProvider = value } } - var underlyingTimelineProvider: RoomTimelineProviderProtocol! + var underlyingTimelineProvider: TimelineProviderProtocol! //MARK: - subscribeForUpdates diff --git a/ElementX/Sources/Mocks/RoomTimelineControllerFactoryMock.swift b/ElementX/Sources/Mocks/RoomTimelineControllerFactoryMock.swift deleted file mode 100644 index 5f6d7bbe2..000000000 --- a/ElementX/Sources/Mocks/RoomTimelineControllerFactoryMock.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files in the repository root for full details. -// - -import Foundation - -struct RoomTimelineControllerFactoryMockConfiguration { - var timelineController: RoomTimelineControllerProtocol? -} - -extension RoomTimelineControllerFactoryMock { - convenience init(configuration: RoomTimelineControllerFactoryMockConfiguration) { - self.init() - - buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue = configuration.timelineController ?? { - let timelineController = MockRoomTimelineController() - timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk - return timelineController - }() - } -} diff --git a/ElementX/Sources/Mocks/TimelineControllerFactoryMock.swift b/ElementX/Sources/Mocks/TimelineControllerFactoryMock.swift new file mode 100644 index 000000000..cb8b32367 --- /dev/null +++ b/ElementX/Sources/Mocks/TimelineControllerFactoryMock.swift @@ -0,0 +1,24 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Foundation + +extension TimelineControllerFactoryMock { + struct Configuration { + var timelineController: TimelineControllerProtocol? + } + + convenience init(_ configuration: Configuration) { + self.init() + + buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue = configuration.timelineController ?? { + let timelineController = MockTimelineController() + timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk + return timelineController + }() + } +} diff --git a/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift b/ElementX/Sources/Mocks/TimelineProviderMock.swift similarity index 95% rename from ElementX/Sources/Mocks/RoomTimelineProviderMock.swift rename to ElementX/Sources/Mocks/TimelineProviderMock.swift index 2ffc5bcd7..73f10c0ed 100644 --- a/ElementX/Sources/Mocks/RoomTimelineProviderMock.swift +++ b/ElementX/Sources/Mocks/TimelineProviderMock.swift @@ -10,7 +10,7 @@ import Foundation import MatrixRustSDK @MainActor -class AutoUpdatingRoomTimelineProviderMock: RoomTimelineProvider { +class AutoUpdatingTimelineProviderMock: TimelineProvider { static var timelineListener: TimelineListener? private let innerPaginationStatePublisher: PassthroughSubject diff --git a/ElementX/Sources/Mocks/TimelineProxyMock.swift b/ElementX/Sources/Mocks/TimelineProxyMock.swift index 183a94932..08faa97a2 100644 --- a/ElementX/Sources/Mocks/TimelineProxyMock.swift +++ b/ElementX/Sources/Mocks/TimelineProxyMock.swift @@ -24,9 +24,9 @@ extension TimelineProxyMock { sendReadReceiptForTypeReturnValue = .success(()) if configuration.isAutoUpdating { - underlyingTimelineProvider = AutoUpdatingRoomTimelineProviderMock() + underlyingTimelineProvider = AutoUpdatingTimelineProviderMock() } else { - let timelineProvider = RoomTimelineProviderMock() + let timelineProvider = TimelineProviderMock() timelineProvider.paginationState = .init(backward: configuration.timelineStartReached ? .timelineEndReached : .idle, forward: .timelineEndReached) timelineProvider.underlyingMembershipChangePublisher = PassthroughSubject().eraseToAnyPublisher() underlyingTimelineProvider = timelineProvider diff --git a/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewModifier.swift b/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewModifier.swift index a4e286101..801e9132a 100644 --- a/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewModifier.swift +++ b/ElementX/Sources/Screens/FilePreviewScreen/TimelineMediaPreviewModifier.swift @@ -163,7 +163,7 @@ struct TimelineMediaPreviewModifier_Previews: PreviewProvider { thumbnailSource: nil, contentType: .pdf)) - let timelineController = MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen)) + let timelineController = MockTimelineController(timelineKind: .media(.mediaFilesScreen)) timelineController.timelineItems = [item] let mediaProvider = MediaProviderMock(configuration: .init()) diff --git a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewDetailsView.swift b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewDetailsView.swift index 14788639b..cdd65880b 100644 --- a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewDetailsView.swift +++ b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewDetailsView.swift @@ -223,7 +223,7 @@ struct TimelineMediaPreviewDetailsView_Previews: PreviewProvider, TestablePrevie contentType: contentType)) let timelineKind = TimelineKind.media(isPresentedOnRoomScreen ? .roomScreen : .mediaFilesScreen) - let timelineController = MockRoomTimelineController(timelineKind: timelineKind) + let timelineController = MockTimelineController(timelineKind: timelineKind) timelineController.timelineItems = [item] let viewModel = TimelineMediaPreviewViewModel(initialItem: item, diff --git a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift index 5f5234ac0..94688d8d2 100644 --- a/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift +++ b/ElementX/Sources/Screens/FilePreviewScreen/View/TimelineMediaPreviewRedactConfirmationView.swift @@ -144,7 +144,7 @@ struct TimelineMediaPreviewRedactConfirmationView_Previews: PreviewProvider, Tes thumbnailInfo: .mockThumbnail, contentType: contentType)) - let timelineController = MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen)) + let timelineController = MockTimelineController(timelineKind: .media(.mediaFilesScreen)) timelineController.timelineItems = [item] return TimelineMediaPreviewViewModel(initialItem: item, timelineViewModel: TimelineViewModel.mock(timelineKind: timelineController.timelineKind, diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenCoordinator.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenCoordinator.swift index f24c39c37..efcdfb6c2 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenCoordinator.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/MediaEventsTimelineScreenCoordinator.swift @@ -10,14 +10,15 @@ import SwiftUI struct MediaEventsTimelineScreenCoordinatorParameters { let roomProxy: JoinedRoomProxyProtocol - let mediaTimelineController: RoomTimelineControllerProtocol - let filesTimelineController: RoomTimelineControllerProtocol + let mediaTimelineController: TimelineControllerProtocol + let filesTimelineController: TimelineControllerProtocol let mediaProvider: MediaProviderProtocol let mediaPlayerProvider: MediaPlayerProviderProtocol let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol let appMediator: AppMediatorProtocol let emojiProvider: EmojiProviderProtocol let userIndicatorController: UserIndicatorControllerProtocol + let timelineControllerFactory: TimelineControllerFactoryProtocol } enum MediaEventsTimelineScreenCoordinatorAction { @@ -47,7 +48,8 @@ final class MediaEventsTimelineScreenCoordinator: CoordinatorProtocol { appMediator: parameters.appMediator, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: parameters.emojiProvider) + emojiProvider: parameters.emojiProvider, + timelineControllerFactory: parameters.timelineControllerFactory) let filesTimelineViewModel = TimelineViewModel(roomProxy: parameters.roomProxy, timelineController: parameters.filesTimelineController, @@ -58,7 +60,8 @@ final class MediaEventsTimelineScreenCoordinator: CoordinatorProtocol { appMediator: parameters.appMediator, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: parameters.emojiProvider) + emojiProvider: parameters.emojiProvider, + timelineControllerFactory: parameters.timelineControllerFactory) viewModel = MediaEventsTimelineScreenViewModel(mediaTimelineViewModel: mediaTimelineViewModel, filesTimelineViewModel: filesTimelineViewModel, diff --git a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift index 2055fcd65..40d4e294e 100644 --- a/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift +++ b/ElementX/Sources/Screens/MediaEventsTimelineScreen/View/MediaEventsTimelineScreen.swift @@ -259,9 +259,9 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview { private static func makeTimelineViewModel(empty: Bool) -> TimelineViewModel { let timelineController = if empty { - MockRoomTimelineController.emptyMediaGallery + MockTimelineController.emptyMediaGallery } else { - MockRoomTimelineController.mediaGallery + MockTimelineController.mediaGallery } return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), @@ -273,6 +273,7 @@ struct MediaEventsTimelineScreen_Previews: PreviewProvider, TestablePreview { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) } } diff --git a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift index 897cad602..9b0a1a30f 100644 --- a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift +++ b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/PinnedEventsTimelineScreenCoordinator.swift @@ -10,12 +10,13 @@ import SwiftUI struct PinnedEventsTimelineScreenCoordinatorParameters { let roomProxy: JoinedRoomProxyProtocol - let timelineController: RoomTimelineControllerProtocol + let timelineController: TimelineControllerProtocol let mediaProvider: MediaProviderProtocol let mediaPlayerProvider: MediaPlayerProviderProtocol let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol let appMediator: AppMediatorProtocol let emojiProvider: EmojiProviderProtocol + let timelineControllerFactory: TimelineControllerFactoryProtocol } enum PinnedEventsTimelineScreenCoordinatorAction { @@ -51,7 +52,8 @@ final class PinnedEventsTimelineScreenCoordinator: CoordinatorProtocol { appMediator: parameters.appMediator, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: parameters.emojiProvider) + emojiProvider: parameters.emojiProvider, + timelineControllerFactory: parameters.timelineControllerFactory) } func start() { diff --git a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift index 4ac1e8305..4827f0267 100644 --- a/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift +++ b/ElementX/Sources/Screens/PinnedEventsTimelineScreen/View/PinnedEventsTimelineScreen.swift @@ -87,7 +87,7 @@ struct PinnedEventsTimelineScreen: View { struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview { static let viewModel = PinnedEventsTimelineScreenViewModel(analyticsService: ServiceLocator.shared.analytics) static let emptyTimelineViewModel: TimelineViewModel = { - let timelineController = MockRoomTimelineController(timelineKind: .pinned) + let timelineController = MockTimelineController(timelineKind: .pinned) timelineController.timelineItems = [] return TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), timelineController: timelineController, @@ -98,7 +98,8 @@ struct PinnedEventsTimelineScreen_Previews: PreviewProvider, TestablePreview { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) }() static var previews: some View { diff --git a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift index baba3beb9..2286754a3 100644 --- a/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomDetailsScreen/RoomDetailsScreenViewModel.swift @@ -21,7 +21,7 @@ class RoomDetailsScreenViewModel: RoomDetailsScreenViewModelType, RoomDetailsScr private let appSettings: AppSettings private var dmRecipient: RoomMemberProxyProtocol? - private var pinnedEventsTimelineProvider: RoomTimelineProviderProtocol? { + private var pinnedEventsTimelineProvider: TimelineProviderProtocol? { didSet { guard let pinnedEventsTimelineProvider else { return diff --git a/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenCoordinator.swift b/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenCoordinator.swift index d87a338a0..e3ef4ac18 100644 --- a/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenCoordinator.swift @@ -10,7 +10,7 @@ import SwiftUI struct RoomPollsHistoryScreenCoordinatorParameters { let pollInteractionHandler: PollInteractionHandlerProtocol - let roomTimelineController: RoomTimelineControllerProtocol + let timelineController: TimelineControllerProtocol } enum RoomPollsHistoryScreenCoordinatorAction { @@ -28,7 +28,7 @@ final class RoomPollsHistoryScreenCoordinator: CoordinatorProtocol { init(parameters: RoomPollsHistoryScreenCoordinatorParameters) { viewModel = RoomPollsHistoryScreenViewModel(pollInteractionHandler: parameters.pollInteractionHandler, - roomTimelineController: parameters.roomTimelineController, + timelineController: parameters.timelineController, userIndicatorController: ServiceLocator.shared.userIndicatorController) } diff --git a/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenViewModel.swift b/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenViewModel.swift index 8c4cbe966..0437b0943 100644 --- a/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomPollsHistoryScreen/RoomPollsHistoryScreenViewModel.swift @@ -18,7 +18,7 @@ class RoomPollsHistoryScreenViewModel: RoomPollsHistoryScreenViewModelType, Room } private let pollInteractionHandler: PollInteractionHandlerProtocol - private let roomTimelineController: RoomTimelineControllerProtocol + private let timelineController: TimelineControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol private var paginateBackwardsTask: Task? @@ -31,10 +31,10 @@ class RoomPollsHistoryScreenViewModel: RoomPollsHistoryScreenViewModelType, Room } init(pollInteractionHandler: PollInteractionHandlerProtocol, - roomTimelineController: RoomTimelineControllerProtocol, + timelineController: TimelineControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol) { self.pollInteractionHandler = pollInteractionHandler - self.roomTimelineController = roomTimelineController + self.timelineController = timelineController self.userIndicatorController = userIndicatorController super.init(initialViewState: RoomPollsHistoryScreenViewState(title: L10n.screenPollsHistoryTitle, @@ -65,7 +65,7 @@ class RoomPollsHistoryScreenViewModel: RoomPollsHistoryScreenViewModelType, Room // MARK: - Private private func setupSubscriptions() { - roomTimelineController.callbacks + timelineController.callbacks .receive(on: DispatchQueue.main) .sink { [weak self] callback in guard let self else { return } @@ -132,7 +132,7 @@ class RoomPollsHistoryScreenViewModel: RoomPollsHistoryScreenViewModelType, Room private func updatePollsList(filter: RoomPollsHistoryFilter) { // Get the poll timeline items to display var items: [PollRoomTimelineItem] = [] - for timelineItem in roomTimelineController.timelineItems { + for timelineItem in timelineController.timelineItems { if let pollRoomTimelineItem = timelineItem as? PollRoomTimelineItem { // Apply the filter switch filter { @@ -148,7 +148,7 @@ class RoomPollsHistoryScreenViewModel: RoomPollsHistoryScreenViewModelType, Room // Map into RoomPollsHistoryPollDetails to have both the event timestamp and the timeline item state.pollTimelineItems = items.map { item in - guard let timestamp = roomTimelineController.eventTimestamp(for: item.id) else { + guard let timestamp = timelineController.eventTimestamp(for: item.id) else { return nil } return RoomPollsHistoryPollDetails(timestamp: timestamp, item: item) @@ -167,7 +167,7 @@ class RoomPollsHistoryScreenViewModel: RoomPollsHistoryScreenViewModelType, Room return } state.isBackPaginating = true - switch await roomTimelineController.paginateBackwards(requestSize: Constants.backPaginationEventLimit) { + switch await timelineController.paginateBackwards(requestSize: Constants.backPaginationEventLimit) { case .failure(let error): MXLog.error("failed to back paginate. \(error)") default: diff --git a/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift b/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift index e47b8a90b..ab0e13849 100644 --- a/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift +++ b/ElementX/Sources/Screens/RoomPollsHistoryScreen/View/RoomPollsHistoryScreen.swift @@ -114,33 +114,33 @@ private extension DateFormatter { struct RoomPollsHistoryScreen_Previews: PreviewProvider, TestablePreview { static let viewModelEmpty: RoomPollsHistoryScreenViewModel = { - let roomTimelineController = MockRoomTimelineController() - roomTimelineController.timelineItems = [] + let timelineController = MockTimelineController() + timelineController.timelineItems = [] let roomProxyMockConfiguration = JoinedRoomProxyMockConfiguration(name: "Polls") let viewModel = RoomPollsHistoryScreenViewModel(pollInteractionHandler: PollInteractionHandlerMock(), - roomTimelineController: roomTimelineController, + timelineController: timelineController, userIndicatorController: UserIndicatorControllerMock()) return viewModel }() static let viewModel: RoomPollsHistoryScreenViewModel = { - let roomTimelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() let polls = [PollRoomTimelineItem.mock(poll: .disclosed(createdByAccountOwner: false)), PollRoomTimelineItem.mock(poll: .disclosed(createdByAccountOwner: true)), PollRoomTimelineItem.mock(poll: .emptyDisclosed, isEditable: true)] - roomTimelineController.timelineItems = polls + timelineController.timelineItems = polls for i in 0.. = .init() @@ -54,7 +56,7 @@ class TimelineInteractionHandler { private var resumeVoiceMessagePlaybackAfterScrubbing = false init(roomProxy: JoinedRoomProxyProtocol, - timelineController: RoomTimelineControllerProtocol, + timelineController: TimelineControllerProtocol, mediaProvider: MediaProviderProtocol, mediaPlayerProvider: MediaPlayerProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, @@ -62,7 +64,9 @@ class TimelineInteractionHandler { userIndicatorController: UserIndicatorControllerProtocol, appMediator: AppMediatorProtocol, appSettings: AppSettings, - analyticsService: AnalyticsService) { + analyticsService: AnalyticsService, + emojiProvider: EmojiProviderProtocol, + timelineControllerFactory: TimelineControllerFactoryProtocol) { self.roomProxy = roomProxy self.timelineController = timelineController self.mediaProvider = mediaProvider @@ -73,6 +77,8 @@ class TimelineInteractionHandler { self.appMediator = appMediator self.appSettings = appSettings self.analyticsService = analyticsService + self.emojiProvider = emojiProvider + self.timelineControllerFactory = timelineControllerFactory pollInteractionHandler = PollInteractionHandler(analyticsService: analyticsService, roomProxy: roomProxy) } @@ -495,7 +501,7 @@ class TimelineInteractionHandler { actionsSubject.send(.displayEmojiPicker(itemID: itemID, selectedEmojis: selectedEmojis)) } - func processItemTap(_ itemID: TimelineItemIdentifier) async -> RoomTimelineControllerAction { + func processItemTap(_ itemID: TimelineItemIdentifier) async -> TimelineControllerAction { guard let timelineItem = timelineController.timelineItems.firstUsingStableID(itemID) else { return .none } @@ -522,7 +528,7 @@ class TimelineInteractionHandler { } } - private func displayMediaActionIfPossible(timelineItem: RoomTimelineItemProtocol) async -> RoomTimelineControllerAction { + private func displayMediaActionIfPossible(timelineItem: RoomTimelineItemProtocol) async -> TimelineControllerAction { var source: MediaSourceProxy? var filename: String var caption: String? diff --git a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift index 4299180c4..c5ad6a2f4 100644 --- a/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift +++ b/ElementX/Sources/Screens/Timeline/TimelineViewModel.swift @@ -22,13 +22,14 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { } private let roomProxy: JoinedRoomProxyProtocol - private let timelineController: RoomTimelineControllerProtocol + private let timelineController: TimelineControllerProtocol private let mediaPlayerProvider: MediaPlayerProviderProtocol private let userIndicatorController: UserIndicatorControllerProtocol private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analyticsService: AnalyticsService private let emojiProvider: EmojiProviderProtocol + private let timelineControllerFactory: TimelineControllerFactoryProtocol private let timelineInteractionHandler: TimelineInteractionHandler @@ -44,7 +45,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { init(roomProxy: JoinedRoomProxyProtocol, focussedEventID: String? = nil, - timelineController: RoomTimelineControllerProtocol, + timelineController: TimelineControllerProtocol, mediaProvider: MediaProviderProtocol, mediaPlayerProvider: MediaPlayerProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, @@ -52,7 +53,8 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { appMediator: AppMediatorProtocol, appSettings: AppSettings, analyticsService: AnalyticsService, - emojiProvider: EmojiProviderProtocol) { + emojiProvider: EmojiProviderProtocol, + timelineControllerFactory: TimelineControllerFactoryProtocol) { self.timelineController = timelineController self.mediaPlayerProvider = mediaPlayerProvider self.roomProxy = roomProxy @@ -61,6 +63,7 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { self.userIndicatorController = userIndicatorController self.appMediator = appMediator self.emojiProvider = emojiProvider + self.timelineControllerFactory = timelineControllerFactory let voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider) @@ -73,7 +76,9 @@ class TimelineViewModel: TimelineViewModelType, TimelineViewModelProtocol { userIndicatorController: userIndicatorController, appMediator: appMediator, appSettings: appSettings, - analyticsService: analyticsService) + analyticsService: analyticsService, + emojiProvider: emojiProvider, + timelineControllerFactory: timelineControllerFactory) super.init(initialViewState: TimelineViewState(timelineKind: timelineController.timelineKind, roomID: roomProxy.id, @@ -865,10 +870,10 @@ private extension RoomInfoProxy { extension TimelineViewModel { static let mock = mock(timelineKind: .live) - static func mock(timelineKind: TimelineKind = .live, timelineController: MockRoomTimelineController? = nil) -> TimelineViewModel { + static func mock(timelineKind: TimelineKind = .live, timelineController: MockTimelineController? = nil) -> TimelineViewModel { TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Preview room")), focussedEventID: nil, - timelineController: timelineController ?? MockRoomTimelineController(timelineKind: timelineKind), + timelineController: timelineController ?? MockTimelineController(timelineKind: timelineKind), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -876,7 +881,8 @@ extension TimelineViewModel { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) } } diff --git a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift index 35c443438..3699d6db4 100644 --- a/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift +++ b/ElementX/Sources/Screens/Timeline/View/ReadReceipts/ReadReceiptsSummaryView.swift @@ -44,7 +44,7 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview { ] let roomProxyMock = JoinedRoomProxyMock(.init(name: "Room", members: members)) let mock = TimelineViewModel(roomProxy: roomProxyMock, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -52,7 +52,8 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) return mock }() diff --git a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift index 75c1642fc..b6a530ccf 100644 --- a/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Style/TimelineItemBubbledStylerView.swift @@ -330,7 +330,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview let roomProxy = JoinedRoomProxyMock(.init(name: "Preview Room", pinnedEventIDs: ["pinned"])) return TimelineViewModel(roomProxy: roomProxy, focussedEventID: nil, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -338,7 +338,8 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider, TestablePreview appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) }() static var previews: some View { diff --git a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift index e7a16c0e6..f8638a16e 100644 --- a/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/Timeline/View/Supplementary/TimelineReadReceiptsView.swift @@ -79,7 +79,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview { ] static let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "Test", members: members)), - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -87,7 +87,8 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) static let singleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now")] static let doubleReceipt = [ReadReceipt(userID: RoomMemberProxyMock.mockAlice.userID, formattedTimestamp: "Now"), diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift index 6f5c87370..7cb63aee6 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineItemViews/HighlightedTimelineItemModifier.swift @@ -89,7 +89,7 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider { static let focussedEventID = "RoomTimelineItemFixtures.default.5" static let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock, focussedEventID: focussedEventID, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -97,7 +97,8 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) static var previews: some View { NavigationStack { diff --git a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift index 007623bfc..f482a4698 100644 --- a/ElementX/Sources/Screens/Timeline/View/TimelineView.swift +++ b/ElementX/Sources/Screens/Timeline/View/TimelineView.swift @@ -82,7 +82,7 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview { name: "Preview room")) static let roomViewModel = RoomScreenViewModel.mock(roomProxyMock: roomProxyMock) static let timelineViewModel = TimelineViewModel(roomProxy: roomProxyMock, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -90,7 +90,8 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) static var previews: some View { NavigationStack { diff --git a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift index 9028074ab..74ee7db13 100644 --- a/ElementX/Sources/Services/Room/JoinedRoomProxy.swift +++ b/ElementX/Sources/Services/Room/JoinedRoomProxy.swift @@ -182,14 +182,15 @@ class JoinedRoomProxy: JoinedRoomProxyProtocol { } } - func messageFilteredTimeline(allowedMessageTypes: [RoomMessageEventMessageType]) async -> Result { + func messageFilteredTimeline(allowedMessageTypes: [RoomMessageEventMessageType], + presentation: TimelineKind.MediaPresentation) async -> Result { do { let sdkTimeline = try await room.timelineWithConfiguration(configuration: .init(focus: .live, allowedMessageTypes: .only(types: allowedMessageTypes), internalIdPrefix: nil, dateDividerMode: .monthly)) - let timeline = TimelineProxy(timeline: sdkTimeline, kind: .media(.mediaFilesScreen)) + let timeline = TimelineProxy(timeline: sdkTimeline, kind: .media(presentation)) await timeline.subscribeForUpdates() return .success(timeline) diff --git a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift index 60e86bb05..02297ad72 100644 --- a/ElementX/Sources/Services/Room/RoomProxyProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomProxyProtocol.swift @@ -78,7 +78,8 @@ protocol JoinedRoomProxyProtocol: RoomProxyProtocol { func timelineFocusedOnEvent(eventID: String, numberOfEvents: UInt16) async -> Result - func messageFilteredTimeline(allowedMessageTypes: [RoomMessageEventMessageType]) async -> Result + func messageFilteredTimeline(allowedMessageTypes: [RoomMessageEventMessageType], + presentation: TimelineKind.MediaPresentation) async -> Result func enableEncryption() async -> Result diff --git a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift similarity index 91% rename from ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift rename to ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift index 12562c44c..f33cfa450 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/MockRoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/MockTimelineController.swift @@ -11,7 +11,7 @@ import Combine import Foundation import MatrixRustSDK -class MockRoomTimelineController: RoomTimelineControllerProtocol { +class MockTimelineController: TimelineControllerProtocol { /// An array of timeline item arrays that will be inserted in order for each back pagination request. var backPaginationResponses: [[RoomTimelineItemProtocol]] = [] /// An array of timeline items that will be appended in order when ``simulateIncomingItems()`` is called. @@ -21,7 +21,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { var roomID: String { roomProxy?.id ?? "MockRoomIdentifier" } var timelineKind: TimelineKind - let callbacks = PassthroughSubject() + let callbacks = PassthroughSubject() var paginationState: PaginationState = .initial { didSet { @@ -34,14 +34,14 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { private var client: UITestsSignalling.Client? - static var mediaGallery: MockRoomTimelineController { - MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen), timelineItems: (0..<5).reduce([]) { partialResult, _ in + static var mediaGallery: MockTimelineController { + MockTimelineController(timelineKind: .media(.mediaFilesScreen), timelineItems: (0..<5).reduce([]) { partialResult, _ in partialResult + [RoomTimelineItemFixtures.separator] + RoomTimelineItemFixtures.mediaChunk }) } - static var emptyMediaGallery: MockRoomTimelineController { - let mock = MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen)) + static var emptyMediaGallery: MockTimelineController { + let mock = MockTimelineController(timelineKind: .media(.mediaFilesScreen)) mock.paginationState = PaginationState(backward: .timelineEndReached, forward: .timelineEndReached) return mock } @@ -63,7 +63,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { } private(set) var focusOnEventCallCount = 0 - func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result { + func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result { focusOnEventCallCount += 1 callbacks.send(.isLive(false)) return .success(()) @@ -75,7 +75,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { callbacks.send(.isLive(true)) } - func paginateBackwards(requestSize: UInt16) async -> Result { + func paginateBackwards(requestSize: UInt16) async -> Result { paginationState = PaginationState(backward: .paginating, forward: .timelineEndReached) if client == nil { @@ -85,7 +85,7 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol { return .success(()) } - func paginateForwards(requestSize: UInt16) async -> Result { + func paginateForwards(requestSize: UInt16) async -> Result { // try? await simulateForwardPagination() .success(()) } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerFactory.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerFactory.swift deleted file mode 100644 index 6f6a12273..000000000 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerFactory.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files in the repository root for full details. -// - -import Foundation -import MatrixRustSDK - -struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol { - func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, - initialFocussedEventID: String?, - timelineItemFactory: RoomTimelineItemFactoryProtocol, - mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol { - RoomTimelineController(roomProxy: roomProxy, - timelineProxy: roomProxy.timeline, - initialFocussedEventID: initialFocussedEventID, - timelineItemFactory: timelineItemFactory, - mediaProvider: mediaProvider, - appSettings: ServiceLocator.shared.settings) - } - - func buildPinnedEventsRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, - timelineItemFactory: RoomTimelineItemFactoryProtocol, - mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? { - guard let pinnedEventsTimeline = await roomProxy.pinnedEventsTimeline else { - return nil - } - - return RoomTimelineController(roomProxy: roomProxy, - timelineProxy: pinnedEventsTimeline, - initialFocussedEventID: nil, - timelineItemFactory: timelineItemFactory, - mediaProvider: mediaProvider, - appSettings: ServiceLocator.shared.settings) - } - - func buildMessageFilteredRoomTimelineController(allowedMessageTypes: [RoomMessageEventMessageType], - roomProxy: JoinedRoomProxyProtocol, - timelineItemFactory: RoomTimelineItemFactoryProtocol, - mediaProvider: MediaProviderProtocol) async -> Result { - switch await roomProxy.messageFilteredTimeline(allowedMessageTypes: allowedMessageTypes) { - case .success(let timelineProxy): - return .success(RoomTimelineController(roomProxy: roomProxy, - timelineProxy: timelineProxy, - initialFocussedEventID: nil, - timelineItemFactory: timelineItemFactory, - mediaProvider: mediaProvider, - appSettings: ServiceLocator.shared.settings)) - case .failure(let error): - return .failure(.roomProxyError(error)) - } - } -} diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerFactoryProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerFactoryProtocol.swift deleted file mode 100644 index ee76bab3e..000000000 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerFactoryProtocol.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright 2022-2024 New Vector Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial -// Please see LICENSE files in the repository root for full details. -// - -import Foundation -import MatrixRustSDK - -enum RoomTimelineFactoryControllerError: Error { - case roomProxyError(RoomProxyError) -} - -@MainActor -protocol RoomTimelineControllerFactoryProtocol { - func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, - initialFocussedEventID: String?, - timelineItemFactory: RoomTimelineItemFactoryProtocol, - mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol - - func buildPinnedEventsRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, - timelineItemFactory: RoomTimelineItemFactoryProtocol, - mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? - - func buildMessageFilteredRoomTimelineController(allowedMessageTypes: [RoomMessageEventMessageType], - roomProxy: JoinedRoomProxyProtocol, - timelineItemFactory: RoomTimelineItemFactoryProtocol, - mediaProvider: MediaProviderProtocol) async -> Result -} - -// sourcery: AutoMockable -extension RoomTimelineControllerFactoryProtocol { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift similarity index 97% rename from ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift rename to ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift index 691073f28..b39e67960 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineController.swift @@ -11,19 +11,19 @@ import IntentsUI import MatrixRustSDK import UIKit -class RoomTimelineController: RoomTimelineControllerProtocol { +class TimelineController: TimelineControllerProtocol { private let roomProxy: JoinedRoomProxyProtocol - private let liveTimelineProvider: RoomTimelineProviderProtocol + private let liveTimelineProvider: TimelineProviderProtocol private let timelineItemFactory: RoomTimelineItemFactoryProtocol private let mediaProvider: MediaProviderProtocol private let appSettings: AppSettings private let serialDispatchQueue: DispatchQueue - let callbacks = PassthroughSubject() + let callbacks = PassthroughSubject() private var activeTimeline: TimelineProxyProtocol - private var activeTimelineProvider: RoomTimelineProviderProtocol { + private var activeTimelineProvider: TimelineProviderProtocol { didSet { configureActiveTimelineProvider() } @@ -57,7 +57,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { self.mediaProvider = mediaProvider self.appSettings = appSettings - serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility) + serialDispatchQueue = DispatchQueue(label: "io.element.elementx.timelineprovider", qos: .utility) activeTimeline = timelineProxy activeTimelineProvider = liveTimelineProvider @@ -82,7 +82,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } - func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result { + func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result { switch await roomProxy.timelineFocusedOnEvent(eventID: eventID, numberOfEvents: timelineSize) { case .success(let timeline): await timeline.subscribeForUpdates() @@ -103,7 +103,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { activeTimelineProvider = liveTimelineProvider } - func paginateBackwards(requestSize: UInt16) async -> Result { + func paginateBackwards(requestSize: UInt16) async -> Result { MXLog.info("Started back pagination request") switch await activeTimeline.paginateBackwards(requestSize: requestSize) { case .success: @@ -115,7 +115,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol { } } - func paginateForwards(requestSize: UInt16) async -> Result { + func paginateForwards(requestSize: UInt16) async -> Result { MXLog.info("Started forward pagination request") switch await activeTimeline.paginateForwards(requestSize: requestSize) { case .success: diff --git a/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerFactory.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerFactory.swift new file mode 100644 index 000000000..092d6706f --- /dev/null +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerFactory.swift @@ -0,0 +1,56 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +struct TimelineControllerFactory: TimelineControllerFactoryProtocol { + func buildTimelineController(roomProxy: JoinedRoomProxyProtocol, + initialFocussedEventID: String?, + timelineItemFactory: RoomTimelineItemFactoryProtocol, + mediaProvider: MediaProviderProtocol) -> TimelineControllerProtocol { + TimelineController(roomProxy: roomProxy, + timelineProxy: roomProxy.timeline, + initialFocussedEventID: initialFocussedEventID, + timelineItemFactory: timelineItemFactory, + mediaProvider: mediaProvider, + appSettings: ServiceLocator.shared.settings) + } + + func buildPinnedEventsTimelineController(roomProxy: JoinedRoomProxyProtocol, + timelineItemFactory: RoomTimelineItemFactoryProtocol, + mediaProvider: MediaProviderProtocol) async -> TimelineControllerProtocol? { + guard let pinnedEventsTimeline = await roomProxy.pinnedEventsTimeline else { + return nil + } + + return TimelineController(roomProxy: roomProxy, + timelineProxy: pinnedEventsTimeline, + initialFocussedEventID: nil, + timelineItemFactory: timelineItemFactory, + mediaProvider: mediaProvider, + appSettings: ServiceLocator.shared.settings) + } + + func buildMessageFilteredTimelineController(allowedMessageTypes: [RoomMessageEventMessageType], + presentation: TimelineKind.MediaPresentation, + roomProxy: JoinedRoomProxyProtocol, + timelineItemFactory: RoomTimelineItemFactoryProtocol, + mediaProvider: MediaProviderProtocol) async -> Result { + switch await roomProxy.messageFilteredTimeline(allowedMessageTypes: allowedMessageTypes, presentation: presentation) { + case .success(let timelineProxy): + return .success(TimelineController(roomProxy: roomProxy, + timelineProxy: timelineProxy, + initialFocussedEventID: nil, + timelineItemFactory: timelineItemFactory, + mediaProvider: mediaProvider, + appSettings: ServiceLocator.shared.settings)) + case .failure(let error): + return .failure(.roomProxyError(error)) + } + } +} diff --git a/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerFactoryProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerFactoryProtocol.swift new file mode 100644 index 000000000..6d15d478a --- /dev/null +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerFactoryProtocol.swift @@ -0,0 +1,34 @@ +// +// Copyright 2022-2024 New Vector Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +// Please see LICENSE files in the repository root for full details. +// + +import Foundation +import MatrixRustSDK + +enum TimelineFactoryControllerError: Error { + case roomProxyError(RoomProxyError) +} + +@MainActor +protocol TimelineControllerFactoryProtocol { + func buildTimelineController(roomProxy: JoinedRoomProxyProtocol, + initialFocussedEventID: String?, + timelineItemFactory: RoomTimelineItemFactoryProtocol, + mediaProvider: MediaProviderProtocol) -> TimelineControllerProtocol + + func buildPinnedEventsTimelineController(roomProxy: JoinedRoomProxyProtocol, + timelineItemFactory: RoomTimelineItemFactoryProtocol, + mediaProvider: MediaProviderProtocol) async -> TimelineControllerProtocol? + + func buildMessageFilteredTimelineController(allowedMessageTypes: [RoomMessageEventMessageType], + presentation: TimelineKind.MediaPresentation, + roomProxy: JoinedRoomProxyProtocol, + timelineItemFactory: RoomTimelineItemFactoryProtocol, + mediaProvider: MediaProviderProtocol) async -> Result +} + +// sourcery: AutoMockable +extension TimelineControllerFactoryProtocol { } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift similarity index 88% rename from ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift rename to ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift index 8aca9248d..834ece1f1 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineControllerProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/TimelineControllerProtocol.swift @@ -9,25 +9,25 @@ import Combine import MatrixRustSDK import SwiftUI -enum RoomTimelineControllerCallback { +enum TimelineControllerCallback { case updatedTimelineItems(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool) case paginationState(PaginationState) case isLive(Bool) } -enum RoomTimelineControllerAction { +enum TimelineControllerAction { case displayMediaFile(file: MediaFileHandleProxy, title: String?) case displayLocation(body: String, geoURI: GeoURI, description: String?) case none } -enum RoomTimelineControllerError: Error { +enum TimelineControllerError: Error { case generic case eventNotFound } @MainActor -protocol RoomTimelineControllerProtocol { +protocol TimelineControllerProtocol { var roomID: String { get } var timelineKind: TimelineKind { get } @@ -37,17 +37,17 @@ protocol RoomTimelineControllerProtocol { /// The current pagination state, use only for setting up the intial state var paginationState: PaginationState { get } - var callbacks: PassthroughSubject { get } + var callbacks: PassthroughSubject { get } func processItemAppearance(_ itemID: TimelineItemIdentifier) async func processItemDisappearance(_ itemID: TimelineItemIdentifier) async - func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result + func focusOnEvent(_ eventID: String, timelineSize: UInt16) async -> Result func focusLive() - func paginateBackwards(requestSize: UInt16) async -> Result - func paginateForwards(requestSize: UInt16) async -> Result + func paginateBackwards(requestSize: UInt16) async -> Result + func paginateForwards(requestSize: UInt16) async -> Result func sendReadReceipt(for itemID: TimelineItemIdentifier) async diff --git a/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift b/ElementX/Sources/Services/Timeline/TimelineProvider.swift similarity index 98% rename from ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift rename to ElementX/Sources/Services/Timeline/TimelineProvider.swift index 1e7107239..607296f1a 100644 --- a/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProvider.swift @@ -9,7 +9,7 @@ import Combine import Foundation import MatrixRustSDK -class RoomTimelineProvider: RoomTimelineProviderProtocol { +class TimelineProvider: TimelineProviderProtocol { private var cancellables = Set() private let serialDispatchQueue: DispatchQueue @@ -46,7 +46,7 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol { } init(timeline: Timeline, kind: TimelineKind, paginationStatePublisher: AnyPublisher) { - serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility) + serialDispatchQueue = DispatchQueue(label: "io.element.elementx.timelineprovider", qos: .utility) itemProxiesSubject = CurrentValueSubject<[TimelineItemProxy], Never>([]) self.kind = kind diff --git a/ElementX/Sources/Services/Timeline/RoomTimelineProviderProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProviderProtocol.swift similarity index 97% rename from ElementX/Sources/Services/Timeline/RoomTimelineProviderProtocol.swift rename to ElementX/Sources/Services/Timeline/TimelineProviderProtocol.swift index a0b09f79f..8e60c7dea 100644 --- a/ElementX/Sources/Services/Timeline/RoomTimelineProviderProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProviderProtocol.swift @@ -29,7 +29,7 @@ struct PaginationState: Equatable { @MainActor // sourcery: AutoMockable -protocol RoomTimelineProviderProtocol { +protocol TimelineProviderProtocol { /// A publisher that signals when ``itemProxies`` or ``paginationState`` are changed. var updatePublisher: AnyPublisher<([TimelineItemProxy], PaginationState), Never> { get } /// The current set of items in the timeline. diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index 8bd82d010..5672cbc9b 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -20,8 +20,8 @@ final class TimelineProxy: TimelineProxyProtocol { private let kind: TimelineKind - private var innerTimelineProvider: RoomTimelineProviderProtocol! - var timelineProvider: RoomTimelineProviderProtocol { + private var innerTimelineProvider: TimelineProviderProtocol! + var timelineProvider: TimelineProviderProtocol { innerTimelineProvider } @@ -47,7 +47,7 @@ final class TimelineProxy: TimelineProxyProtocol { await subscribeToPagination() - let provider = await RoomTimelineProvider(timeline: timeline, kind: kind, paginationStatePublisher: paginationStatePublisher) + let provider = await TimelineProvider(timeline: timeline, kind: kind, paginationStatePublisher: paginationStatePublisher) // Make sure the existing items are built so that we have content in the timeline before // determining whether or not the timeline should paginate to load more items. await provider.waitForInitialItems() diff --git a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift index 586c65a0a..0f90c48f3 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxyProtocol.swift @@ -27,7 +27,7 @@ enum TimelineProxyError: Error { // sourcery: AutoMockable protocol TimelineProxyProtocol { - var timelineProvider: RoomTimelineProviderProtocol { get } + var timelineProvider: TimelineProviderProtocol { get } func subscribeForUpdates() async diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 689f418c2..76adb7854 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -235,7 +235,7 @@ class MockScreen: Identifiable { let navigationStackCoordinator = NavigationStackCoordinator() let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), roomProxy: JoinedRoomProxyMock(.init(name: "Some room name", avatarURL: nil)), - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -244,13 +244,14 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .roomSmallTimeline: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: .mockMXCAvatar)), @@ -263,13 +264,14 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .roomSmallTimelineWithReactions: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.default let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: .mockMXCAvatar)), @@ -282,13 +284,14 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .roomSmallTimelineWithReadReceipts: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.smallChunkWithReadReceipts let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), roomProxy: JoinedRoomProxyMock(.init(name: "New room", avatarURL: .mockMXCAvatar)), @@ -301,14 +304,15 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator case .roomSmallTimelineIncomingAndSmallPagination: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController(listenForSignals: true) + let timelineController = MockTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.singleMessageChunk] timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] @@ -323,7 +327,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -331,7 +336,7 @@ class MockScreen: Identifiable { case .roomSmallTimelineLargePagination: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController(listenForSignals: true) + let timelineController = MockTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.smallChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), @@ -345,7 +350,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -353,7 +359,7 @@ class MockScreen: Identifiable { case .roomLayoutTop: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController(listenForSignals: true) + let timelineController = MockTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), @@ -367,7 +373,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -375,7 +382,7 @@ class MockScreen: Identifiable { case .roomLayoutMiddle: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController(listenForSignals: true) + let timelineController = MockTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk timelineController.backPaginationResponses = [RoomTimelineItemFixtures.largeChunk] timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] @@ -390,7 +397,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -398,7 +406,7 @@ class MockScreen: Identifiable { case .roomLayoutBottom: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController(listenForSignals: true) + let timelineController = MockTimelineController(listenForSignals: true) timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk timelineController.incomingItems = [RoomTimelineItemFixtures.incomingMessage] let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), @@ -412,7 +420,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -420,7 +429,7 @@ class MockScreen: Identifiable { case .roomLayoutHighlight: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.permalinkChunk let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), roomProxy: JoinedRoomProxyMock(.init(name: "Timeline highlight", avatarURL: .mockMXCAvatar)), @@ -433,7 +442,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -454,7 +464,7 @@ class MockScreen: Identifiable { case .roomWithDisclosedPolls: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.disclosedPolls timelineController.incomingItems = [] let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), @@ -468,7 +478,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -476,7 +487,7 @@ class MockScreen: Identifiable { case .roomWithUndisclosedPolls: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.undisclosedPolls timelineController.incomingItems = [] let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), @@ -490,7 +501,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -498,7 +510,7 @@ class MockScreen: Identifiable { case .roomWithOutgoingPolls: let navigationStackCoordinator = NavigationStackCoordinator() - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = RoomTimelineItemFixtures.outgoingPolls timelineController.incomingItems = [] let parameters = RoomScreenCoordinatorParameters(clientProxy: ClientProxyMock(), @@ -512,7 +524,8 @@ class MockScreen: Identifiable { ongoingCallRoomIDPublisher: .init(.init(nil)), appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, - composerDraftService: ComposerDraftServiceMock(.init())) + composerDraftService: ComposerDraftServiceMock(.init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -540,7 +553,7 @@ class MockScreen: Identifiable { appSettings: ServiceLocator.shared.settings), bugReportService: BugReportServiceMock(), elementCallService: ElementCallServiceMock(.init()), - roomTimelineControllerFactory: RoomTimelineControllerFactoryMock(configuration: .init()), + timelineControllerFactory: TimelineControllerFactoryMock(.init()), appMediator: appMediator, appSettings: appSettings, appHooks: AppHooks(), @@ -678,14 +691,14 @@ class MockScreen: Identifiable { clientProxy.roomForIdentifierReturnValue = .joined(roomProxy) - let timelineController = RoomTimelineController(roomProxy: roomProxy, - timelineProxy: roomProxy.timeline, - initialFocussedEventID: nil, - timelineItemFactory: RoomTimelineItemFactory(userID: "@alice:matrix.org", - attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), - stateEventStringBuilder: RoomStateEventStringBuilder(userID: "@alice:matrix.org")), - mediaProvider: MediaProviderMock(configuration: .init()), - appSettings: ServiceLocator.shared.settings) + let timelineController = TimelineController(roomProxy: roomProxy, + timelineProxy: roomProxy.timeline, + initialFocussedEventID: nil, + timelineItemFactory: RoomTimelineItemFactory(userID: "@alice:matrix.org", + attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), + stateEventStringBuilder: RoomStateEventStringBuilder(userID: "@alice:matrix.org")), + mediaProvider: MediaProviderMock(configuration: .init()), + appSettings: ServiceLocator.shared.settings) let flowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), navigationRootCoordinator: navigationRootCoordinator, @@ -693,7 +706,7 @@ class MockScreen: Identifiable { appSettings: ServiceLocator.shared.settings), bugReportService: BugReportServiceMock(), elementCallService: ElementCallServiceMock(.init()), - roomTimelineControllerFactory: RoomTimelineControllerFactoryMock(configuration: .init(timelineController: timelineController)), + timelineControllerFactory: TimelineControllerFactoryMock(.init(timelineController: timelineController)), appMediator: AppMediatorMock.default, appSettings: appSettings, appHooks: AppHooks(), diff --git a/UnitTests/Sources/PillContextTests.swift b/UnitTests/Sources/PillContextTests.swift index 07383fdcc..ec3cb3a9e 100644 --- a/UnitTests/Sources/PillContextTests.swift +++ b/UnitTests/Sources/PillContextTests.swift @@ -18,7 +18,7 @@ class PillContextTests: XCTestCase { let subject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([]) proxyMock.membersPublisher = subject.asCurrentValuePublisher() let mock = TimelineViewModel(roomProxy: proxyMock, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -26,7 +26,8 @@ class PillContextTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body))) XCTAssertFalse(context.viewState.isOwnMention) @@ -47,7 +48,7 @@ class PillContextTests: XCTestCase { let subject = CurrentValueSubject<[RoomMemberProxyProtocol], Never>([]) proxyMock.membersPublisher = subject.asCurrentValuePublisher() let mock = TimelineViewModel(roomProxy: proxyMock, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -55,7 +56,8 @@ class PillContextTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .user(userID: id), font: .preferredFont(forTextStyle: .body))) XCTAssertTrue(context.viewState.isOwnMention) @@ -66,7 +68,7 @@ class PillContextTests: XCTestCase { let id = "test_room" let displayName = "Test" let proxyMock = JoinedRoomProxyMock(.init(id: id, name: displayName, avatarURL: avatarURL)) - let mockController = MockRoomTimelineController() + let mockController = MockTimelineController() mockController.roomProxy = proxyMock let mock = TimelineViewModel(roomProxy: proxyMock, timelineController: mockController, @@ -77,7 +79,8 @@ class PillContextTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let context = PillContext(timelineContext: mock.context, data: PillTextAttachmentData(type: .allUsers, font: .preferredFont(forTextStyle: .body))) XCTAssertTrue(context.viewState.isOwnMention) diff --git a/UnitTests/Sources/RoomFlowCoordinatorTests.swift b/UnitTests/Sources/RoomFlowCoordinatorTests.swift index c3ea9b9f1..dc6e3293b 100644 --- a/UnitTests/Sources/RoomFlowCoordinatorTests.swift +++ b/UnitTests/Sources/RoomFlowCoordinatorTests.swift @@ -13,7 +13,7 @@ import Combine @MainActor class RoomFlowCoordinatorTests: XCTestCase { var clientProxy: ClientProxyMock! - var timelineControllerFactory: RoomTimelineControllerFactoryMock! + var timelineControllerFactory: TimelineControllerFactoryMock! var roomFlowCoordinator: RoomFlowCoordinator! var navigationStackCoordinator: NavigationStackCoordinator! var cancellables = Set() @@ -311,7 +311,7 @@ class RoomFlowCoordinatorTests: XCTestCase { private func setupRoomFlowCoordinator(asChildFlow: Bool = false, roomType: RoomType? = nil) async { cancellables.removeAll() clientProxy = ClientProxyMock(.init(userID: "hi@bob", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) - timelineControllerFactory = RoomTimelineControllerFactoryMock(configuration: .init()) + timelineControllerFactory = TimelineControllerFactoryMock(.init()) clientProxy.roomPreviewForIdentifierViaClosure = { [roomType] roomID, _ in switch roomType { @@ -336,7 +336,7 @@ class RoomFlowCoordinatorTests: XCTestCase { roomFlowCoordinator = await RoomFlowCoordinator(roomID: roomID, userSession: UserSessionMock(.init(clientProxy: clientProxy)), isChildFlow: asChildFlow, - roomTimelineControllerFactory: timelineControllerFactory, + timelineControllerFactory: timelineControllerFactory, navigationStackCoordinator: navigationStackCoordinator, emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), ongoingCallRoomIDPublisher: .init(.init(nil)), diff --git a/UnitTests/Sources/RoomPollsHistoryScreenViewModelTests.swift b/UnitTests/Sources/RoomPollsHistoryScreenViewModelTests.swift index 65d78aede..b468dc1f1 100644 --- a/UnitTests/Sources/RoomPollsHistoryScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomPollsHistoryScreenViewModelTests.swift @@ -13,13 +13,13 @@ import XCTest class RoomPollsHistoryScreenViewModelTests: XCTestCase { var viewModel: RoomPollsHistoryScreenViewModelProtocol! var interactionHandler: PollInteractionHandlerMock! - var timelineController: MockRoomTimelineController! + var timelineController: MockTimelineController! override func setUpWithError() throws { interactionHandler = PollInteractionHandlerMock() - timelineController = MockRoomTimelineController() + timelineController = MockTimelineController() viewModel = RoomPollsHistoryScreenViewModel(pollInteractionHandler: interactionHandler, - roomTimelineController: timelineController, + timelineController: timelineController, userIndicatorController: UserIndicatorControllerMock()) } diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index db2c57cd5..4120c4690 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -66,7 +66,7 @@ class RoomScreenViewModelTests: XCTestCase { // setup the loaded pinned events injection in the timeline let pinnedTimelineMock = TimelineProxyMock() - let pinnedTimelineProviderMock = RoomTimelineProviderMock() + let pinnedTimelineProviderMock = TimelineProviderMock() let providerUpdateSubject = PassthroughSubject<([TimelineItemProxy], PaginationState), Never>() pinnedTimelineProviderMock.underlyingUpdatePublisher = providerUpdateSubject.eraseToAnyPublisher() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock @@ -107,7 +107,7 @@ class RoomScreenViewModelTests: XCTestCase { let roomProxyMock = JoinedRoomProxyMock(.init()) // setup a way to inject the mock of the pinned events timeline let pinnedTimelineMock = TimelineProxyMock() - let pinnedTimelineProviderMock = RoomTimelineProviderMock() + let pinnedTimelineProviderMock = TimelineProviderMock() pinnedTimelineMock.timelineProvider = pinnedTimelineProviderMock pinnedTimelineProviderMock.underlyingUpdatePublisher = Empty<([TimelineItemProxy], PaginationState), Never>().eraseToAnyPublisher() pinnedTimelineProviderMock.itemProxies = [.event(.init(item: EventTimelineItem(configuration: .init(eventID: "test1")), uniqueID: .init(id: "1"))), diff --git a/UnitTests/Sources/TimelineMediaPreviewViewModelTests.swift b/UnitTests/Sources/TimelineMediaPreviewViewModelTests.swift index 312936bc0..0eade742e 100644 --- a/UnitTests/Sources/TimelineMediaPreviewViewModelTests.swift +++ b/UnitTests/Sources/TimelineMediaPreviewViewModelTests.swift @@ -19,7 +19,7 @@ class TimelineMediaPreviewViewModelTests: XCTestCase { var context: TimelineMediaPreviewViewModel.Context { viewModel.context } var mediaProvider: MediaProviderMock! var photoLibraryManager: PhotoLibraryManagerMock! - var timelineController: MockRoomTimelineController! + var timelineController: MockTimelineController! func testLoadingItem() async throws { // Given a fresh view model. @@ -274,7 +274,7 @@ class TimelineMediaPreviewViewModelTests: XCTestCase { private func setupViewModel(initialItemIndex: Int = 0, photoLibraryAuthorizationDenied: Bool = false) { let initialItems = makeItems() - timelineController = MockRoomTimelineController(timelineKind: .media(.mediaFilesScreen)) + timelineController = MockTimelineController(timelineKind: .media(.mediaFilesScreen)) timelineController.timelineItems = initialItems mediaProvider = MediaProviderMock(configuration: .init()) diff --git a/UnitTests/Sources/TimelineViewModelTests.swift b/UnitTests/Sources/TimelineViewModelTests.swift index 81bd54bf1..fbc227aab 100644 --- a/UnitTests/Sources/TimelineViewModelTests.swift +++ b/UnitTests/Sources/TimelineViewModelTests.swift @@ -40,7 +40,7 @@ class TimelineViewModelTests: XCTestCase { ] // When showing them in a timeline. - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -68,7 +68,7 @@ class TimelineViewModelTests: XCTestCase { ] // When showing them in a timeline. - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -94,7 +94,7 @@ class TimelineViewModelTests: XCTestCase { ] // When showing them in a timeline. - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -117,7 +117,7 @@ class TimelineViewModelTests: XCTestCase { ] // When showing them in a timeline. - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -140,7 +140,7 @@ class TimelineViewModelTests: XCTestCase { ] // When showing them in a timeline. - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -157,7 +157,7 @@ class TimelineViewModelTests: XCTestCase { let items = [TextRoomTimelineItem(eventID: "t1"), TextRoomTimelineItem(eventID: "t2"), TextRoomTimelineItem(eventID: "t3")] - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -181,7 +181,7 @@ class TimelineViewModelTests: XCTestCase { let items = [TextRoomTimelineItem(eventID: "t1"), TextRoomTimelineItem(eventID: "t2"), TextRoomTimelineItem(eventID: "t3")] - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -205,7 +205,7 @@ class TimelineViewModelTests: XCTestCase { let items = [TextRoomTimelineItem(eventID: "t1"), TextRoomTimelineItem(eventID: "t2"), TextRoomTimelineItem(eventID: "t3")] - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = items let viewModel = makeViewModel(timelineController: timelineController) @@ -230,7 +230,7 @@ class TimelineViewModelTests: XCTestCase { } func testInitialFocusViewState() async throws { - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() let viewModel = makeViewModel(focussedEventID: "t10", timelineController: timelineController) XCTAssertEqual(viewModel.context.viewState.timelineState.focussedEvent, .init(eventID: "t10", appearance: .immediate)) @@ -289,13 +289,13 @@ class TimelineViewModelTests: XCTestCase { private func readReceiptsConfiguration(with items: [RoomTimelineItemProtocol]) -> (TimelineViewModel, JoinedRoomProxyMock, TimelineProxyMock, - MockRoomTimelineController) { + MockTimelineController) { let roomProxy = JoinedRoomProxyMock(.init(name: "")) let timelineProxy = TimelineProxyMock() roomProxy.timeline = timelineProxy - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineProxy.sendReadReceiptForTypeReturnValue = .success(()) @@ -311,7 +311,8 @@ class TimelineViewModelTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) return (viewModel, roomProxy, timelineProxy, timelineController) } @@ -325,7 +326,7 @@ class TimelineViewModelTests: XCTestCase { let id = message.id // When showing them in a timeline. - let timelineController = MockRoomTimelineController() + let timelineController = MockTimelineController() timelineController.timelineItems = [message] let viewModel = TimelineViewModel(roomProxy: JoinedRoomProxyMock(.init(name: "", members: [RoomMemberProxyMock.mockAlice, RoomMemberProxyMock.mockCharlie])), timelineController: timelineController, @@ -336,7 +337,8 @@ class TimelineViewModelTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) let deferred = deferFulfillment(viewModel.context.$viewState) { value in value.bindings.readReceiptsSummaryInfo?.orderedReceipts == receipts @@ -356,7 +358,7 @@ class TimelineViewModelTests: XCTestCase { roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher() let viewModel = TimelineViewModel(roomProxy: roomProxyMock, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -364,7 +366,8 @@ class TimelineViewModelTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) XCTAssertEqual(configuration.pinnedEventIDs, viewModel.context.viewState.pinnedEventIDs) configuration.pinnedEventIDs = ["test1", "test2"] @@ -382,7 +385,7 @@ class TimelineViewModelTests: XCTestCase { roomProxyMock.underlyingInfoPublisher = infoSubject.asCurrentValuePublisher() let viewModel = TimelineViewModel(roomProxy: roomProxyMock, - timelineController: MockRoomTimelineController(), + timelineController: MockTimelineController(), mediaProvider: MediaProviderMock(configuration: .init()), mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), @@ -390,7 +393,8 @@ class TimelineViewModelTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) var deferred = deferFulfillment(viewModel.context.$viewState) { value in value.canCurrentUserPin @@ -409,7 +413,7 @@ class TimelineViewModelTests: XCTestCase { private func makeViewModel(roomProxy: JoinedRoomProxyProtocol? = nil, focussedEventID: String? = nil, - timelineController: RoomTimelineControllerProtocol) -> TimelineViewModel { + timelineController: TimelineControllerProtocol) -> TimelineViewModel { TimelineViewModel(roomProxy: roomProxy ?? JoinedRoomProxyMock(.init(name: "")), focussedEventID: focussedEventID, timelineController: timelineController, @@ -420,7 +424,8 @@ class TimelineViewModelTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, - emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings)) + emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings), + timelineControllerFactory: TimelineControllerFactoryMock(.init())) } } diff --git a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift index 4eab3fab1..96272b876 100644 --- a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift +++ b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift @@ -13,7 +13,7 @@ import Combine @MainActor class UserSessionFlowCoordinatorTests: XCTestCase { var clientProxy: ClientProxyMock! - var timelineControllerFactory: RoomTimelineControllerFactoryMock! + var timelineControllerFactory: TimelineControllerFactoryMock! var userSessionFlowCoordinator: UserSessionFlowCoordinator! var navigationRootCoordinator: NavigationRootCoordinator! var notificationManager: NotificationManagerMock! @@ -27,7 +27,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase { override func setUp() async throws { cancellables.removeAll() clientProxy = ClientProxyMock(.init(userID: "hi@bob", roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loaded(.mockRooms))))) - timelineControllerFactory = RoomTimelineControllerFactoryMock(configuration: .init()) + timelineControllerFactory = TimelineControllerFactoryMock(.init()) navigationRootCoordinator = NavigationRootCoordinator() @@ -38,7 +38,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase { appLockService: AppLockServiceMock(), bugReportService: BugReportServiceMock(), elementCallService: ElementCallServiceMock(.init()), - roomTimelineControllerFactory: timelineControllerFactory, + timelineControllerFactory: timelineControllerFactory, appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, appHooks: AppHooks(), @@ -211,8 +211,8 @@ class UserSessionFlowCoordinatorTests: XCTestCase { XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator) XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0) XCTAssertNotNil(detailCoordinator) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 1) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "1") + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 1) + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "1") // A child event route should push a new room screen onto the stack and focus on the event. userSessionFlowCoordinator.handleAppRoute(.childEvent(eventID: "2", roomID: "2", via: []), animated: true) @@ -221,24 +221,24 @@ class UserSessionFlowCoordinatorTests: XCTestCase { XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 1) XCTAssertTrue(detailNavigationStack?.stackCoordinators.first is RoomScreenCoordinator) XCTAssertNotNil(detailCoordinator) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 2) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "2") + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 2) + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "2") // A subsequent regular event route should clear the stack and set the new room as the root of the stack. try await process(route: .event(eventID: "3", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3")) XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator) XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0) XCTAssertNotNil(detailCoordinator) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 3) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "3") + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 3) + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "3") // A regular event route for the same room should set a new instance of the room as the root of the stack. try await process(route: .event(eventID: "4", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3")) XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator) XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0) XCTAssertNotNil(detailCoordinator) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 4) - XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "4", + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 4) + XCTAssertEqual(timelineControllerFactory.buildTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "4", "A new timeline should be created for the same room ID, so that the screen isn't stale while loading.") }