diff --git a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift index c15edc1c2..d5d5c2822 100644 --- a/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift +++ b/AccessibilityTests/Sources/GeneratedAccessibilityTests.swift @@ -403,8 +403,8 @@ extension AccessibilityTests { try await performAccessibilityAudit(named: "ModerationAndSafetySettingsScreen_Previews") } - func testMultiAccountAnnouncementScreen() async throws { - try await performAccessibilityAudit(named: "MultiAccountAnnouncementScreen_Previews") + func testMultiAccountAnnouncementView() async throws { + try await performAccessibilityAudit(named: "MultiAccountAnnouncementView_Previews") } func testNoticeRoomTimelineView() async throws { diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 3584917d0..5046a393b 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -533,7 +533,6 @@ 52BE121FC21CE88803D2D8B5 /* GalleryItemTileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7927284C6F70D60BE5949F8C /* GalleryItemTileView.swift */; }; 5341D48F833E3E30F16FA2A3 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2910422CB628D3B2BBE47449 /* SeparatorRoomTimelineView.swift */; }; 5375902175B2FEA2949D7D74 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CDDDDD9FE1A699D23A5E096 /* LoginScreen.swift */; }; - 53A182049BEEC6195B91DD79 /* MultiAccountAnnouncementScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69630272FD26D4E5974CC88E /* MultiAccountAnnouncementScreenViewModelProtocol.swift */; }; 53A59720F4729D9BBFFB7CAB /* NotificationSettingsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD9CB3B9DFA353AB2B7CD9F8 /* NotificationSettingsEditScreenCoordinator.swift */; }; 53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */; }; 53DEF39F0C4DE02E3FC56D91 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; }; @@ -1045,7 +1044,6 @@ A0646F23876D6326AD27FF52 /* LiveLocationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA2FEFA393FC7D2870263012 /* LiveLocationSession.swift */; }; A07178337F3C0B208B5A77A8 /* NotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6ED50FE104992419310EEB /* NotificationHandler.swift */; }; A076E0A9338FD2D950C3C4A1 /* ChatsSpaceFiltersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E63072981793CCA84EE12798 /* ChatsSpaceFiltersScreenViewModelProtocol.swift */; }; - A0790A97DAD44E8895035704 /* MultiAccountAnnouncementScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD46D21FF370EDF6BE87F8A7 /* MultiAccountAnnouncementScreenCoordinator.swift */; }; A0861B727B273B5B3DD7FBF6 /* KnockRequestsListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09227E671DB30795C43FFFD /* KnockRequestsListScreenViewModel.swift */; }; A0868BDE84D2140A885BE3C9 /* EncryptionResetScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8562F4D7DE073BC32902AB /* EncryptionResetScreenViewModelProtocol.swift */; }; A0D7E5BD0298A97DCBDCE40B /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 7731767AE437BA3BD2CC14A8 /* Sentry */; }; @@ -1103,6 +1101,7 @@ A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; }; A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; }; A88328D7E17F73AB64501B51 /* SwiftOGG in Frameworks */ = {isa = PBXBuildFile; productRef = 391D11F92DFC91666AA1503F /* SwiftOGG */; }; + A88A0C11EB4231EE2989C659 /* MultiAccountAnnouncementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5BE5CAC17E09EE2643D97F2 /* MultiAccountAnnouncementView.swift */; }; A8DB299C5C891EDAE74A22F4 /* UserLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F7482D5D1526E3399B00174 /* UserLocationCell.swift */; }; A8E324E700E596E36B0A311B /* BootDetectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F054DE7D47849687662C9D9 /* BootDetectionManager.swift */; }; A8FA7671948E3DF27F320026 /* BugReportFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7367B3B9A8CAF902220F31D1 /* BugReportFlowCoordinator.swift */; }; @@ -1289,7 +1288,6 @@ C8E11A335456FCF94A744E6E /* SpaceFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDDE826EAB1BAB80C1104980 /* SpaceFlowCoordinator.swift */; }; C8E1E4E06B7C7A3A8246FC9B /* MediaEventsTimelineScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8512B82404B1751D0BCC82D2 /* MediaEventsTimelineScreenCoordinator.swift */; }; C900127318820AD04D6C90B8 /* LabsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E43D8784B0054C048060FEB /* LabsScreenModels.swift */; }; - C90A5ABB868F7DDEE2DFD653 /* MultiAccountAnnouncementScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DB71240A4E855DE7B9201FD /* MultiAccountAnnouncementScreenModels.swift */; }; C915347779B3C7FDD073A87A /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93E1FF0DFBB3768F79FDBF6D /* AVMetadataMachineReadableCodeObjectExtensionsTest.swift */; }; C95ABC529E0009926BDE5664 /* preview_image.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 44688A16934827A0D1668253 /* preview_image.jpg */; }; C960BACE42A9D8C535E8CB34 /* Pagination.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1912062B53CE95E6F700DA60 /* Pagination.swift */; }; @@ -1422,7 +1420,6 @@ DCFE7CB3B9A104330BBB96AD /* AnalyticsPromptScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B67DF223EEB8DCAF178A1D4 /* AnalyticsPromptScreenCoordinator.swift */; }; DD21CE51DF9BD04FC8155972 /* LeaveSpaceHandleSDKMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580BDCD23DD02481AB5FFB47 /* LeaveSpaceHandleSDKMock.swift */; }; DD4D8B5D668197D345A3ED42 /* TimelineControllerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = F933BB25FC426D5C67E1D812 /* TimelineControllerMock.swift */; }; - DD66C863B4232BCDEBE883C8 /* MultiAccountAnnouncementScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 94B5FD54003D9527FE347516 /* MultiAccountAnnouncementScreen.swift */; }; DDE7B4771452300C103B1EB8 /* RoomDirectoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F6210134203BE1F2DD5C679 /* RoomDirectoryCell.swift */; }; DDFBDEE1DC32BDD5488F898C /* ClientProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */; }; DE3BF0ED68E56BF625591D49 /* LinkNewDeviceScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72C4A2D279386A811BDC6DAE /* LinkNewDeviceScreen.swift */; }; @@ -1586,7 +1583,6 @@ F519DE17A3A0F760307B2E6D /* InviteUsersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02D155E09BF961BBA8F85263 /* InviteUsersScreenViewModel.swift */; }; F54E2D6CAD96E1AC15BC526F /* MessageForwardingScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */; }; F58BF3BD3233A03F013816E4 /* ClassicAppAccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F4DFFFC62187D9A4D2030D /* ClassicAppAccountManager.swift */; }; - F5A74869B9899FB178EDCDF9 /* MultiAccountAnnouncementScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F6FA4E8F2A0D6947AA44C2 /* MultiAccountAnnouncementScreenViewModel.swift */; }; F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */; }; F656F92A63D3DC1978D79427 /* Macros in Frameworks */ = {isa = PBXBuildFile; productRef = 401E93551F7792B0179907CF /* Macros */; }; F65F3909769E7C48B3309D9D /* RemoteSettingsHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D186A6DB8FAC5C9D0E4D61 /* RemoteSettingsHook.swift */; }; @@ -2086,7 +2082,6 @@ 2DA3DBE1A42EAFF93889FA04 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/SAS.strings; sourceTree = ""; }; 2DA4F09CB613C54FDC73AE6A /* ThreadDecorator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadDecorator.swift; sourceTree = ""; }; 2DB0E533508094156D8024C3 /* TimelineStyler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyler.swift; sourceTree = ""; }; - 2DB71240A4E855DE7B9201FD /* MultiAccountAnnouncementScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiAccountAnnouncementScreenModels.swift; sourceTree = ""; }; 2E11E7C396ED06A154CF6DF3 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/SAS.strings; sourceTree = ""; }; 2F06F70B9C433BAD4BC6B9F5 /* EncryptedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptedRoomTimelineView.swift; sourceTree = ""; }; 2F36C5D9B37E50915ECBD3EE /* RoomMemberProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxy.swift; sourceTree = ""; }; @@ -2204,7 +2199,6 @@ 42C58159916ABD7F454CE689 /* AdvancedSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenCoordinator.swift; sourceTree = ""; }; 42C64A14EE89928207E3B42B /* AnalyticsSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenModels.swift; sourceTree = ""; }; 42C8C368A611B9CB79C7F5FA /* RoomPollsHistoryScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreen.swift; sourceTree = ""; }; - 42F6FA4E8F2A0D6947AA44C2 /* MultiAccountAnnouncementScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiAccountAnnouncementScreenViewModel.swift; sourceTree = ""; }; 430C73079A84654BF46A7FF5 /* FileMediaEventsTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileMediaEventsTimelineView.swift; sourceTree = ""; }; 4338BB055A26094673CDC220 /* SpaceAddRoomsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceAddRoomsScreenViewModel.swift; sourceTree = ""; }; 434522ED2BDED08759048077 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; @@ -2432,7 +2426,6 @@ 68F8744C32ACA0821A771EBB /* test_audio.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = test_audio.mp3; sourceTree = ""; }; 690915157A874D1F21ADE70C /* preview_avatar_room.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = preview_avatar_room.jpg; sourceTree = ""; }; 693E16574C6F7F9FA1015A8C /* Search.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Search.swift; sourceTree = ""; }; - 69630272FD26D4E5974CC88E /* MultiAccountAnnouncementScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiAccountAnnouncementScreenViewModelProtocol.swift; sourceTree = ""; }; 69A05E85E4872C3221C5C287 /* RemotePreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemotePreference.swift; sourceTree = ""; }; 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregatedReactionMock.swift; sourceTree = ""; }; 69D42EE0102D2857933625DD /* CreateRoomViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModelTests.swift; sourceTree = ""; }; @@ -2691,7 +2684,6 @@ 94028A227645FA880B966211 /* WaveformSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaveformSource.swift; sourceTree = ""; }; 9475FD81B13D50103E5290EB /* SpaceSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceSettingsScreen.swift; sourceTree = ""; }; 94A561C542596126DAE211AD /* preview_avatar_user.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = preview_avatar_user.jpg; sourceTree = ""; }; - 94B5FD54003D9527FE347516 /* MultiAccountAnnouncementScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiAccountAnnouncementScreen.swift; sourceTree = ""; }; 94D670124FC3E84F23A62CCF /* APNSPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APNSPayload.swift; sourceTree = ""; }; 94EE2C5F0A06F146BBE3A1B1 /* LinkNewDeviceFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkNewDeviceFlowCoordinator.swift; sourceTree = ""; }; 9501D11B4258DFA33BA3B40F /* ServerSelectionScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenModels.swift; sourceTree = ""; }; @@ -2791,6 +2783,7 @@ A4D9DF4F2DF3507F99B5B97B /* LabsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabsScreenViewModel.swift; sourceTree = ""; }; A54AAF72E821B4084B7E4298 /* PinnedEventsTimelineFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsTimelineFlowCoordinator.swift; sourceTree = ""; }; A566F8F2C27B99E1FB80C69B /* JoinRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRule.swift; sourceTree = ""; }; + A5BE5CAC17E09EE2643D97F2 /* MultiAccountAnnouncementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiAccountAnnouncementView.swift; sourceTree = ""; }; A5C45578F406698388B97C07 /* AuthenticationClassicAppAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationClassicAppAccountView.swift; sourceTree = ""; }; A63A7C1486AA8FF088B1701C /* ModerationAndSafetySettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModerationAndSafetySettingsScreenCoordinator.swift; sourceTree = ""; }; A67BCEFE35F8D00CADF108C9 /* AccountProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountProvider.swift; sourceTree = ""; }; @@ -3046,7 +3039,6 @@ CCE06F4A71FD46C9D8CD432E /* RoomThreadListServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomThreadListServiceProxy.swift; sourceTree = ""; }; CCF71646898A2F720C5BFDF5 /* RoomDirectorySearchScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenViewModel.swift; sourceTree = ""; }; CD469F7513574341181F7EAA /* ServerSelectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreen.swift; sourceTree = ""; }; - CD46D21FF370EDF6BE87F8A7 /* MultiAccountAnnouncementScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiAccountAnnouncementScreenCoordinator.swift; sourceTree = ""; }; CD6613DE16AD26B3A74DA1F5 /* LocationRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationRoomTimelineItemContent.swift; sourceTree = ""; }; CDA38C9B58693A49A3032552 /* DeclineAndBlockScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeclineAndBlockScreenCoordinator.swift; sourceTree = ""; }; CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = ""; }; @@ -4016,14 +4008,6 @@ path = RoomMember; sourceTree = ""; }; - 2C5E4DC51BC09EA05CA1D590 /* View */ = { - isa = PBXGroup; - children = ( - 94B5FD54003D9527FE347516 /* MultiAccountAnnouncementScreen.swift */, - ); - path = View; - sourceTree = ""; - }; 2D0D49B0533C4C2EB889BF3A /* ServerSelectionScreen */ = { isa = PBXGroup; children = ( @@ -4860,6 +4844,7 @@ 05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */, ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */, C7661EFFCAA307A97D71132A /* HomeScreenRoomList.swift */, + A5BE5CAC17E09EE2643D97F2 /* MultiAccountAnnouncementView.swift */, 037A5661B26EC6BE068188D7 /* Filters */, ); path = View; @@ -5086,18 +5071,6 @@ path = View; sourceTree = ""; }; - 601F598160369994A6C0C85D /* MultiAccountAnnouncementScreen */ = { - isa = PBXGroup; - children = ( - CD46D21FF370EDF6BE87F8A7 /* MultiAccountAnnouncementScreenCoordinator.swift */, - 2DB71240A4E855DE7B9201FD /* MultiAccountAnnouncementScreenModels.swift */, - 42F6FA4E8F2A0D6947AA44C2 /* MultiAccountAnnouncementScreenViewModel.swift */, - 69630272FD26D4E5974CC88E /* MultiAccountAnnouncementScreenViewModelProtocol.swift */, - 2C5E4DC51BC09EA05CA1D590 /* View */, - ); - path = MultiAccountAnnouncementScreen; - sourceTree = ""; - }; 605F8221E52991786397FCC9 /* View */ = { isa = PBXGroup; children = ( @@ -7248,7 +7221,6 @@ 87E2774157D9C4894BCFF3F8 /* MediaPickerScreen */, 23605DD08620BE6558242469 /* MediaUploadPreviewScreen */, 3348D14DBDB54E72FC67E2F3 /* MessageForwardingScreen */, - 601F598160369994A6C0C85D /* MultiAccountAnnouncementScreen */, DE07A244ACE83E40D894EDF3 /* NativeCallScreen */, 8F074E22FD93E64211971845 /* Onboarding */, A448A3A8F764174C60CD0CA1 /* Other */, @@ -9287,11 +9259,7 @@ 70A4C56E4E6C60A3124631FC /* ModerationAndSafetySettingsScreenModels.swift in Sources */, 5846723185B444D52E077874 /* ModerationAndSafetySettingsScreenViewModel.swift in Sources */, 7C8F9E3AF56E7FF5B338DBFA /* ModerationAndSafetySettingsScreenViewModelProtocol.swift in Sources */, - DD66C863B4232BCDEBE883C8 /* MultiAccountAnnouncementScreen.swift in Sources */, - A0790A97DAD44E8895035704 /* MultiAccountAnnouncementScreenCoordinator.swift in Sources */, - C90A5ABB868F7DDEE2DFD653 /* MultiAccountAnnouncementScreenModels.swift in Sources */, - F5A74869B9899FB178EDCDF9 /* MultiAccountAnnouncementScreenViewModel.swift in Sources */, - 53A182049BEEC6195B91DD79 /* MultiAccountAnnouncementScreenViewModelProtocol.swift in Sources */, + A88A0C11EB4231EE2989C659 /* MultiAccountAnnouncementView.swift in Sources */, E938F7A45D6A3DBBE6789A03 /* NSEUserSession.swift in Sources */, 551E03A935A6B73158A1AB4A /* NSEUserSessionMock.swift in Sources */, F9842667B68DC6FA1F9ECCBB /* NSItemProvider.swift in Sources */, diff --git a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift index 3b97007fb..c6f389434 100644 --- a/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift +++ b/ElementX/Sources/Other/TestablePreview/TestablePreviewsDictionary.swift @@ -105,7 +105,7 @@ enum TestablePreviewsDictionary { "MessageForwardingScreen_Previews" : MessageForwardingScreen_Previews.self, "MessageText_Previews" : MessageText_Previews.self, "ModerationAndSafetySettingsScreen_Previews" : ModerationAndSafetySettingsScreen_Previews.self, - "MultiAccountAnnouncementScreen_Previews" : MultiAccountAnnouncementScreen_Previews.self, + "MultiAccountAnnouncementView_Previews" : MultiAccountAnnouncementView_Previews.self, "NoticeRoomTimelineView_Previews" : NoticeRoomTimelineView_Previews.self, "NotificationPermissionsScreen_Previews" : NotificationPermissionsScreen_Previews.self, "NotificationSettingsEditScreenRoomCell_Previews" : NotificationSettingsEditScreenRoomCell_Previews.self, diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index b984ad2d4..afc2ca8e6 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -42,6 +42,10 @@ enum HomeScreenViewAction { case resetEncryption case skipRecoveryKeyConfirmation case dismissNewSoundBanner + case screenAppeared + case multiAccountAnnouncementAppeared + case dismissMultiAccountAnnouncement + case addAccount case updateVisibleItemRange(Range) case spaceFilters case markRoomAsUnread(roomIdentifier: String) @@ -161,6 +165,7 @@ struct HomeScreenViewStateBindings { var leaveRoomAlertItem: LeaveRoomAlertItem? var spaceFiltersViewModel: ChatsSpaceFiltersScreenViewModel? + var isPresentingMultiAccountAnnouncement = false } enum CallBadgeType { diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 24ff5fc0a..764685b3a 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -218,6 +218,14 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol state.securityBannerMode = .dismissed case .dismissNewSoundBanner: userSettings.hasSeenNewSoundBanner = true + case .screenAppeared: + // Use a task otherwise the presentation isn't animated. + Task { presentMultiAccountAnnouncementIfNeeded() } + case .multiAccountAnnouncementAppeared: + MXLog.info("The multi-account announcement has been seen.") + userSettings.hasSeenMultiAccountAnnouncement = true + case .dismissMultiAccountAnnouncement, .addAccount: // There is no add account flow to start yet. + state.bindings.isPresentingMultiAccountAnnouncement = false case .updateVisibleItemRange(let range): roomSummaryProvider?.updateVisibleRange(range) case .startChat: @@ -476,6 +484,21 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol actionsSubject.send(.roomLeft(roomIdentifier: roomID)) } + // MARK: Multi-account announcement + + private func presentMultiAccountAnnouncementIfNeeded() { + // An unverified session gets the identity confirmation cover first, the room list appears again once it's dismissed. + guard userSettings.multiAccountEnabled, + !userSettings.hasSeenMultiAccountAnnouncement, + userSession.sessionSecurityStatePublisher.value.verificationState == .verified, + !state.bindings.isPresentingMultiAccountAnnouncement else { + return + } + + MXLog.info("Presenting the multi-account announcement.") + state.bindings.isPresentingMultiAccountAnnouncement = true + } + // MARK: Invites private func acceptInvite(roomID: String) async { diff --git a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift index fbd9b0a17..e68f4c32a 100644 --- a/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift +++ b/ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift @@ -42,6 +42,11 @@ struct HomeScreen: View { .navigationTransition(.zoom(sourceID: NavigationTransitionSourceID.spaceFilters, in: navigationTransitionNamespace)) } + .sheet(isPresented: $context.isPresentingMultiAccountAnnouncement) { + MultiAccountAnnouncementView(context: context) + .onAppear { context.send(viewAction: .multiAccountAnnouncementAppeared) } + } + .onAppear { context.send(viewAction: .screenAppeared) } } // MARK: - Private diff --git a/ElementX/Sources/Screens/HomeScreen/View/MultiAccountAnnouncementView.swift b/ElementX/Sources/Screens/HomeScreen/View/MultiAccountAnnouncementView.swift new file mode 100644 index 000000000..61296350e --- /dev/null +++ b/ElementX/Sources/Screens/HomeScreen/View/MultiAccountAnnouncementView.swift @@ -0,0 +1,85 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// + +import Compound +import SwiftUI + +struct MultiAccountAnnouncementView: View { + let context: HomeScreenViewModel.Context + + @State private var navigationBarHeight: CGFloat = .zero + @State private var contentHeight: CGFloat = .zero + @State private var buttonHeight: CGFloat = .zero + + private var subtitle: AttributedString { + let boldPlaceholder = "{bold}" + var subtitle = AttributedString(L10n.screenMultiAccountAnnouncementDescription(boldPlaceholder)) + var boldString = AttributedString("\(L10n.commonSettings) > \(L10n.screenSettingsAddAccount)") + boldString.bold() + subtitle.replace(boldPlaceholder, with: boldString) + return subtitle + } + + var body: some View { + GeometryReader { geometry in + ElementNavigationStack { + ScrollView { + content + .readHeight($contentHeight) + } + .scrollBounceBehavior(.basedOnSize) + .backportSafeAreaBar(edge: .bottom, spacing: 0) { + addAccountButton + .padding(.bottom, geometry.safeAreaInsets.bottom > 0 ? 0 : 16) + .readHeight($buttonHeight) + } + .onGeometryChange(for: CGFloat.self) { $0.safeAreaInsets.top } action: { navigationBarHeight = $0 } + .toolbar { + ToolbarItem(placement: .primaryAction) { + ToolbarButton(role: .close) { context.send(viewAction: .dismissMultiAccountAnnouncement) } + } + } + } + } + .presentationDetents([.height(navigationBarHeight + contentHeight + buttonHeight)]) + .presentationDragIndicator(.hidden) + .presentationBackground(.compound.bgCanvasDefault) + } + + private var content: some View { + TitleAndIcon(title: L10n.screenMultiAccountAnnouncementTitle, + subtitle: subtitle, + icon: \.userProfileSolid, + iconStyle: .defaultSolid) + .frame(maxWidth: .infinity) + .padding(.horizontal, 24) + .padding(.top, 8) + .padding(.bottom, 24) + } + + private var addAccountButton: some View { + Button { + context.send(viewAction: .addAccount) + } label: { + Label(L10n.screenMultiAccountAnnouncementAction, icon: \.plus) + } + .buttonStyle(.compound(.primary)) + .padding(.horizontal, 16) + .padding(.top, 16) + } +} + +// MARK: - Previews + +struct MultiAccountAnnouncementView_Previews: PreviewProvider, TestablePreview { + static let viewModel = HomeScreen_Previews.viewModel(.rooms) + + static var previews: some View { + MultiAccountAnnouncementView(context: viewModel.context) + .previewDisplayName("Default") + } +} diff --git a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenCoordinator.swift b/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenCoordinator.swift deleted file mode 100644 index 3bc124d6e..000000000 --- a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenCoordinator.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright 2026 Element Creations Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. -// Please see LICENSE files in the repository root for full details. -// - -import Combine -import SwiftUI - -enum MultiAccountAnnouncementScreenCoordinatorAction { - case addAccount - case dismiss -} - -final class MultiAccountAnnouncementScreenCoordinator: CoordinatorProtocol { - private let viewModel: MultiAccountAnnouncementScreenViewModelProtocol - - private var cancellables = Set() - - private let actionsSubject: PassthroughSubject = .init() - var actionsPublisher: AnyPublisher { - actionsSubject.eraseToAnyPublisher() - } - - init() { - viewModel = MultiAccountAnnouncementScreenViewModel() - } - - func start() { - viewModel.actionsPublisher.sink { [weak self] action in - guard let self else { return } - - switch action { - case .addAccount: - actionsSubject.send(.addAccount) - case .dismiss: - actionsSubject.send(.dismiss) - } - } - .store(in: &cancellables) - } - - func toPresentable() -> AnyView { - AnyView(MultiAccountAnnouncementScreen(context: viewModel.context)) - } -} diff --git a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenModels.swift b/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenModels.swift deleted file mode 100644 index e8c55b81c..000000000 --- a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenModels.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// Copyright 2026 Element Creations Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. -// Please see LICENSE files in the repository root for full details. -// - -import SwiftUI - -enum MultiAccountAnnouncementScreenViewModelAction { - case addAccount - case dismiss -} - -struct MultiAccountAnnouncementScreenViewState: BindableState { - let description: AttributedString - - init() { - let boldPlaceholder = "{bold}" - var description = AttributedString(L10n.screenMultiAccountAnnouncementDescription(boldPlaceholder)) - var boldString = AttributedString("\(L10n.commonSettings) > \(L10n.screenSettingsAddAccount)") - boldString.bold() - description.replace(boldPlaceholder, with: boldString) - self.description = description - } -} - -enum MultiAccountAnnouncementScreenViewAction { - case addAccount - case close -} diff --git a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenViewModel.swift b/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenViewModel.swift deleted file mode 100644 index 7cac7e8ff..000000000 --- a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenViewModel.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// Copyright 2026 Element Creations Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. -// Please see LICENSE files in the repository root for full details. -// - -import Combine -import SwiftUI - -typealias MultiAccountAnnouncementScreenViewModelType = StateStoreViewModelV2 - -class MultiAccountAnnouncementScreenViewModel: MultiAccountAnnouncementScreenViewModelType, MultiAccountAnnouncementScreenViewModelProtocol { - private let actionsSubject: PassthroughSubject = .init() - var actionsPublisher: AnyPublisher { - actionsSubject.eraseToAnyPublisher() - } - - init() { - super.init(initialViewState: .init()) - } - - // MARK: - Public - - override func process(viewAction: MultiAccountAnnouncementScreenViewAction) { - switch viewAction { - case .addAccount: - actionsSubject.send(.addAccount) - case .close: - actionsSubject.send(.dismiss) - } - } -} diff --git a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenViewModelProtocol.swift b/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenViewModelProtocol.swift deleted file mode 100644 index eafeb99d2..000000000 --- a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/MultiAccountAnnouncementScreenViewModelProtocol.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// Copyright 2026 Element Creations Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. -// Please see LICENSE files in the repository root for full details. -// - -import Combine - -protocol MultiAccountAnnouncementScreenViewModelProtocol { - var actionsPublisher: AnyPublisher { get } - var context: MultiAccountAnnouncementScreenViewModelType.Context { get } -} diff --git a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/View/MultiAccountAnnouncementScreen.swift b/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/View/MultiAccountAnnouncementScreen.swift deleted file mode 100644 index 9b9dbc480..000000000 --- a/ElementX/Sources/Screens/MultiAccountAnnouncementScreen/View/MultiAccountAnnouncementScreen.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// Copyright 2026 Element Creations Ltd. -// -// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. -// Please see LICENSE files in the repository root for full details. -// - -import Compound -import SwiftUI - -struct MultiAccountAnnouncementScreen: View { - let context: MultiAccountAnnouncementScreenViewModel.Context - - @State private var contentHeight: CGFloat = .zero - @State private var buttonHeight: CGFloat = .zero - private let topPadding: CGFloat = 44 // For the navigation bar - - var body: some View { - ElementNavigationStack { - ScrollView { - content - .readHeight($contentHeight) - } - .scrollBounceBehavior(.basedOnSize) - .backportSafeAreaBar(edge: .bottom, spacing: 0) { - addAccountButton - .readHeight($buttonHeight) - } - .toolbar { - ToolbarItem(placement: .primaryAction) { - ToolbarButton(role: .close) { context.send(viewAction: .close) } - } - } - } - .presentationDetents([.height(contentHeight + buttonHeight + topPadding)]) - .presentationDragIndicator(.hidden) - .presentationBackground(.compound.bgCanvasDefault) - } - - private var content: some View { - TitleAndIcon(title: L10n.screenMultiAccountAnnouncementTitle, - subtitle: context.viewState.description, - icon: \.userProfileSolid, - iconStyle: .defaultSolid) - .frame(maxWidth: .infinity) - .padding(.horizontal, 24) - .padding(.top, 16) - .padding(.bottom, 24) - } - - private var addAccountButton: some View { - Button { - context.send(viewAction: .addAccount) - } label: { - Label(L10n.screenMultiAccountAnnouncementAction, icon: \.plus) - } - .buttonStyle(.compound(.primary)) - .padding(16) - } -} - -// MARK: - Previews - -struct MultiAccountAnnouncementScreen_Previews: PreviewProvider, TestablePreview { - static let viewModel = MultiAccountAnnouncementScreenViewModel() - - static var previews: some View { - MultiAccountAnnouncementScreen(context: viewModel.context) - .previewDisplayName("Default") - } -} diff --git a/PreviewTests/Sources/GeneratedPreviewTests.swift b/PreviewTests/Sources/GeneratedPreviewTests.swift index edcf34a8c..3239295f1 100644 --- a/PreviewTests/Sources/GeneratedPreviewTests.swift +++ b/PreviewTests/Sources/GeneratedPreviewTests.swift @@ -705,8 +705,8 @@ extension PreviewTests { } @Test - func multiAccountAnnouncementScreen() async throws { - for (index, preview) in MultiAccountAnnouncementScreen_Previews._allPreviews.enumerated() { + func multiAccountAnnouncementView() async throws { + for (index, preview) in MultiAccountAnnouncementView_Previews._allPreviews.enumerated() { try await assertSnapshots(matching: preview, step: index) } } diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPad-en-GB.png deleted file mode 100644 index b658ecf53..000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPad-en-GB.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:257f3948490a4a9ca4b9f2eefebb3f0c010bfb3fae2721e4b95cab52889fe3a5 -size 102613 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPad-pseudo.png deleted file mode 100644 index cf0adf860..000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPad-pseudo.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2588daf8fa9a61b445545e047799520bbd20388a5dbd3f2a71ad94438aee6149 -size 118735 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPhone-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPhone-en-GB.png deleted file mode 100644 index 3cc32d349..000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPhone-en-GB.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:753970b5fa26d39052106d90d2f6ecc807a15eddf031f5f67c6e7457d188675a -size 63416 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPhone-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPhone-pseudo.png deleted file mode 100644 index ddfb9f471..000000000 --- a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementScreen.Default-iPhone-pseudo.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d987ab496eec5bfe41d3c1b8f2143da225b5740c7b79eb75d4dfaa7fa4b379c2 -size 85276 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPad-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPad-en-GB.png new file mode 100644 index 000000000..f45ec9b90 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPad-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7967692a14b42d638ab62437ffeddb16e3256d416c601447dc0bedb13f3047f2 +size 102541 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPad-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPad-pseudo.png new file mode 100644 index 000000000..75e98c346 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPad-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c5532a443cbd0cb308a854685d4e3a6f0025127592c11d195f6b0a0f89641c4 +size 118672 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPhone-en-GB.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPhone-en-GB.png new file mode 100644 index 000000000..88c9100b0 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPhone-en-GB.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31d9c55229100d0b51bfeaa91fd5fb2800a36da17d4a38b794a057070bfb0ec2 +size 63289 diff --git a/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPhone-pseudo.png b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPhone-pseudo.png new file mode 100644 index 000000000..bb2a79063 --- /dev/null +++ b/PreviewTests/Sources/__Snapshots__/PreviewTests/multiAccountAnnouncementView.Default-iPhone-pseudo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eae27d5df83718a448c85764713f2bea34367933885aaf484028eb6534207520 +size 85098 diff --git a/UnitTests/Sources/HomeScreenViewModelTests.swift b/UnitTests/Sources/HomeScreenViewModelTests.swift index 8ed6465df..260c0809c 100644 --- a/UnitTests/Sources/HomeScreenViewModelTests.swift +++ b/UnitTests/Sources/HomeScreenViewModelTests.swift @@ -380,10 +380,92 @@ final class HomeScreenViewModelTests { #expect(userSettings.hasSeenNewSoundBanner) } + @Test + func multiAccountAnnouncement() async throws { + userSettings.multiAccountEnabled = true + setupViewModel() + + // Arming it after the view model exists (Developer options) doesn't present it on its own. + userSettings.hasSeenMultiAccountAnnouncement = false + let deferredFailure = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.bindings.isPresentingMultiAccountAnnouncement } + try await deferredFailure.fulfill() + + let deferred = deferFulfillment(context.$viewState) { $0.bindings.isPresentingMultiAccountAnnouncement } + context.send(viewAction: .screenAppeared) + try await deferred.fulfill() + #expect(!userSettings.hasSeenMultiAccountAnnouncement) + + context.send(viewAction: .multiAccountAnnouncementAppeared) + #expect(userSettings.hasSeenMultiAccountAnnouncement) + #expect(context.viewState.bindings.isPresentingMultiAccountAnnouncement) + + // A swipe down dismisses it without any of the announcement's actions. + context.isPresentingMultiAccountAnnouncement = false + let deferredReappearance = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.bindings.isPresentingMultiAccountAnnouncement } + context.send(viewAction: .screenAppeared) + try await deferredReappearance.fulfill() + } + + @Test(arguments: [HomeScreenViewAction.dismissMultiAccountAnnouncement, .addAccount]) + func multiAccountAnnouncementActionsDismissIt(_ action: HomeScreenViewAction) async throws { + userSettings.multiAccountEnabled = true + userSettings.hasSeenMultiAccountAnnouncement = false + setupViewModel() + + let deferred = deferFulfillment(context.$viewState) { $0.bindings.isPresentingMultiAccountAnnouncement } + context.send(viewAction: .screenAppeared) + try await deferred.fulfill() + + context.send(viewAction: action) + #expect(!context.viewState.bindings.isPresentingMultiAccountAnnouncement) + } + + @Test(arguments: MultiAccountAnnouncementHiddenCase.allCases) + func multiAccountAnnouncementHidden(_ hiddenCase: MultiAccountAnnouncementHiddenCase) async throws { + userSettings.multiAccountEnabled = hiddenCase != .flagOff + userSettings.hasSeenMultiAccountAnnouncement = hiddenCase == .alreadySeen + let verificationState: SessionVerificationState = switch hiddenCase { + case .unverified: .unverified + case .unknownVerificationState: .unknown + case .flagOff, .alreadySeen: .verified + } + let securityStateSubject = CurrentValueSubject(.init(verificationState: verificationState, recoveryState: .enabled)) + setupViewModel(securityStatePublisher: securityStateSubject.asCurrentValuePublisher()) + + let deferred = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.bindings.isPresentingMultiAccountAnnouncement } + context.send(viewAction: .screenAppeared) + try await deferred.fulfill() + #expect(userSettings.hasSeenMultiAccountAnnouncement == (hiddenCase == .alreadySeen)) + } + + @Test + func multiAccountAnnouncementPresentsOnTheNextAppearanceOnceVerified() async throws { + userSettings.multiAccountEnabled = true + userSettings.hasSeenMultiAccountAnnouncement = false + let securityStateSubject = CurrentValueSubject(.init(verificationState: .unverified, recoveryState: .enabled)) + setupViewModel(securityStatePublisher: securityStateSubject.asCurrentValuePublisher()) + + let deferredFailure = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.bindings.isPresentingMultiAccountAnnouncement } + context.send(viewAction: .screenAppeared) + try await deferredFailure.fulfill() + + // Becoming verified doesn't present it on its own… + securityStateSubject.send(.init(verificationState: .verified, recoveryState: .enabled)) + let deferredVerifiedFailure = deferFailure(context.$viewState, timeout: .seconds(1)) { $0.bindings.isPresentingMultiAccountAnnouncement } + try await deferredVerifiedFailure.fulfill() + + // …the room list appearing again does, e.g. once the identity confirmation cover is dismissed. + let deferred = deferFulfillment(context.$viewState) { $0.bindings.isPresentingMultiAccountAnnouncement } + context.send(viewAction: .screenAppeared) + try await deferred.fulfill() + } + // MARK: - Helpers enum InviteType { case rooms, spaces } + enum MultiAccountAnnouncementHiddenCase: CaseIterable { case flagOff, alreadySeen, unverified, unknownVerificationState } + @Test func roomListModeIsSetSynchronouslyOnAWarmLaunch() { setupViewModel()