diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 350ba3f48..a801a0366 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -221,6 +221,7 @@ 61941DEE5F3834765770BE01 /* InviteUsersScreenSelectedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10F32E0B4B83D2A11EE8D011 /* InviteUsersScreenSelectedItem.swift */; }; 61A36B9BB2ADE36CEFF5E98C /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E93A1BE7D8A2EBCAD51EEB4 /* Array.swift */; }; 6298AB0906DDD3525CD78C6B /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = 81DB3AB6CE996AB3954F4F03 /* KZFileWatchers */; }; + 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */; }; 6448F8D1D3CA4CD27BB4CADD /* RoomMemberProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F36C5D9B37E50915ECBD3EE /* RoomMemberProxy.swift */; }; 64C373ACCFA26D42BA45CFAD /* HomeScreenInvitesButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24227FF9A2797F6EA7F69CDD /* HomeScreenInvitesButton.swift */; }; 64D05250CEDE8B604119F6E6 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981663D961C94270FA035FD0 /* Alert.swift */; }; @@ -258,6 +259,7 @@ 706289B086B0A6B0C211763F /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; }; 706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */; }; 719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; }; + 71B62C48B8079D49F3FBC845 /* ExpiringTaskRunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B7A28A6606D58D1E38C55A /* ExpiringTaskRunnerTests.swift */; }; 71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */; }; 7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; }; 7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */; }; @@ -309,6 +311,7 @@ 85AFBB433AD56704A880F8A0 /* FramePreferenceKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4798B3B7A1E8AE3901CEE8C6 /* FramePreferenceKey.swift */; }; 85F89F3F320F4FADCFFFE68B /* ServerSelectionScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3059CFA00C67D8787273B20 /* ServerSelectionScreenViewModel.swift */; }; 864C69CF951BF36D25BE0C03 /* DeveloperOptionsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D0A27607AB09784C8501B5C /* DeveloperOptionsScreenViewModelTests.swift */; }; + 8658F5034EAD7357CE7F9AC7 /* MatrixUserShareLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */; }; 86675910612A12409262DFBD /* SessionVerificationStateMachineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C22B1B5FA3A765EADB2CC9 /* SessionVerificationStateMachineTests.swift */; }; 8691186F9B99BCDDB7CACDD8 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */; }; 872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9349F590E35CE514A71E6764 /* LoginHomeserver.swift */; }; @@ -385,7 +388,6 @@ 9FAF6DA7E8E85C9699757764 /* CollapsibleRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E2656184491C505700D2405 /* CollapsibleRoomTimelineView.swift */; }; A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */; }; A0A0D2A9564BDA3FDE2E360F /* FormattedBodyText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */; }; - A0DF6EC32A17869500FC97FB /* MatrixUserShareLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0DF6EC22A17869500FC97FB /* MatrixUserShareLink.swift */; }; A14A9419105A1CD42F0511C4 /* UserIndicatorModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43005941B3A2C9671E23C85 /* UserIndicatorModalView.swift */; }; A17FAD2EBC53E17B5FD384DB /* InviteUsersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22730A30C50AC2E3D5BA8642 /* InviteUsersScreenViewModelProtocol.swift */; }; A182920710146E5BEAA1A705 /* AnalyticsSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7DBA101D643B31E813F3AC1 /* AnalyticsSettingsScreen.swift */; }; @@ -719,7 +721,7 @@ 1222DB76B917EB8A55365BA5 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = ""; }; 127A57D053CE8C87B5EFB089 /* Consumable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Consumable.swift; sourceTree = ""; }; 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = ""; }; - 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = IntegrationTests.xctestplan; sourceTree = ""; }; + 1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = ""; }; 130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = ""; }; 13673F95EBA78D40C09CCE35 /* MockUserIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockUserIndicatorController.swift; sourceTree = ""; }; 13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = ""; }; @@ -830,7 +832,7 @@ 46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsAppCoordinator.swift; sourceTree = ""; }; 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomProxyProtocol.swift; sourceTree = ""; }; 471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineView.swift; sourceTree = ""; }; - 478BE8591BD13E908EF70C0C /* DesignKit */ = {isa = PBXFileReference; lastKnownFileType = folder; path = DesignKit; sourceTree = SOURCE_ROOT; }; + 478BE8591BD13E908EF70C0C /* DesignKit */ = {isa = PBXFileReference; lastKnownFileType = folder; name = DesignKit; path = DesignKit; sourceTree = SOURCE_ROOT; }; 4798B3B7A1E8AE3901CEE8C6 /* FramePreferenceKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramePreferenceKey.swift; sourceTree = ""; }; 47E6DD75A81D07CD91997D8C /* SettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenViewModelProtocol.swift; sourceTree = ""; }; 47EBB5D698CE9A25BB553A2D /* Strings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strings.swift; sourceTree = ""; }; @@ -850,6 +852,7 @@ 4FD6E621CC5E6D4830D96D2D /* MockMediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaProvider.swift; sourceTree = ""; }; 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = ""; }; 5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItem.swift; sourceTree = ""; }; + 50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixUserShareLink.swift; sourceTree = ""; }; 51C2BCE0BC1FC69C1B36E688 /* BugReportScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportScreenModels.swift; sourceTree = ""; }; 5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = ""; }; 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedString.swift; sourceTree = ""; }; @@ -933,6 +936,7 @@ 7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportViewModelTests.swift; sourceTree = ""; }; 7B048F159E9E4C29A7257905 /* AnalyticsSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenViewModelProtocol.swift; sourceTree = ""; }; 7B04BD3874D736127A8156B8 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; + 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiringTaskRunner.swift; sourceTree = ""; }; 7B849D2FF2CC12BA411A1651 /* CreateRoomModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomModels.swift; sourceTree = ""; }; 7D0CBC76C80E04345E11F2DB /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = ""; }; 7D25A35764C7B3DB78954AB5 /* RoomTimelineItemFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemFactoryProtocol.swift; sourceTree = ""; }; @@ -953,6 +957,7 @@ 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = ""; }; 84816E0D2F34E368BF64FA60 /* SessionVerificationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreen.swift; sourceTree = ""; }; 84A87D0471D438A233C2CF4A /* RoomMemberDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenViewModel.swift; sourceTree = ""; }; + 84B7A28A6606D58D1E38C55A /* ExpiringTaskRunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpiringTaskRunnerTests.swift; sourceTree = ""; }; 854BCEAF2A832176FAACD2CB /* SplashScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenCoordinator.swift; sourceTree = ""; }; 85890C78055B786CCABC9194 /* AnalyticsSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreenModels.swift; sourceTree = ""; }; 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderProtocol.swift; sourceTree = ""; }; @@ -968,7 +973,7 @@ 8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilder.swift; sourceTree = ""; }; 8D6094DEAAEB388E1AE118C6 /* MockRoomTimelineProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineProvider.swift; sourceTree = ""; }; 8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = ""; }; - 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = ""; }; + 8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = ""; }; 8E1BBA73B611EDEEA6E20E05 /* InvitesScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenModels.swift; sourceTree = ""; }; 8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenUITests.swift; sourceTree = ""; }; 8F61A0DD8243B395499C99A2 /* InvitesScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenUITests.swift; sourceTree = ""; }; @@ -1011,7 +1016,6 @@ A00C7A331B72C0F05C00392F /* RoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelProtocol.swift; sourceTree = ""; }; A05707BF550D770168A406DB /* LoginViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewModelTests.swift; sourceTree = ""; }; A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerProtocol.swift; sourceTree = ""; }; - A0DF6EC22A17869500FC97FB /* MatrixUserShareLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixUserShareLink.swift; sourceTree = ""; }; A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorControllerProtocol.swift; sourceTree = ""; }; A16CD2C62CB7DB78A4238485 /* ReportContentScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenCoordinator.swift; sourceTree = ""; }; A1C22B1B5FA3A765EADB2CC9 /* SessionVerificationStateMachineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationStateMachineTests.swift; sourceTree = ""; }; @@ -1063,7 +1067,7 @@ B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemContextMenu.swift; sourceTree = ""; }; B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = ""; }; B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = ""; }; - B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = ""; }; + B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = ""; }; B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = ""; }; B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; B7DBA101D643B31E813F3AC1 /* AnalyticsSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreen.swift; sourceTree = ""; }; @@ -1127,7 +1131,7 @@ CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryProvider.swift; sourceTree = ""; }; CECF45B5E8E795666B8C5013 /* SettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenModels.swift; sourceTree = ""; }; CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = ""; }; - CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = ""; }; + CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = ""; }; CF48AF076424DBC1615C74AD /* AuthenticationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationServiceProxy.swift; sourceTree = ""; }; D06A27D9C70E0DCC1E199163 /* OnboardingBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackgroundView.swift; sourceTree = ""; }; D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = ""; }; @@ -1189,7 +1193,7 @@ ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenViewModel.swift; sourceTree = ""; }; ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = ""; }; ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = ""; }; - ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = ""; }; + ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = ""; }; ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineView.swift; sourceTree = ""; }; EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = ""; }; EE378083653EF0C9B5E9D580 /* EmoteRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItemContent.swift; sourceTree = ""; }; @@ -1595,7 +1599,7 @@ 0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */, B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */, C352359663A0E52BA20761EE /* LoadableImage.swift */, - A0DF6EC22A17869500FC97FB /* MatrixUserShareLink.swift */, + 50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */, C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */, 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */, 923485F85E1D765EF9D20E88 /* UserProfileCell.swift */, @@ -2144,6 +2148,7 @@ DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */, 9BF9E3E6A23180EC05F06460 /* EmojiMartJSONLoaderTests.swift */, 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */, + 84B7A28A6606D58D1E38C55A /* ExpiringTaskRunnerTests.swift */, DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */, 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */, CC14E5209C262530E19BC4C1 /* InvitesScreenViewModelTests.swift */, @@ -2731,6 +2736,7 @@ AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */, 127A57D053CE8C87B5EFB089 /* Consumable.swift */, 127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */, + 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */, 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */, 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */, C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */, @@ -3511,6 +3517,7 @@ 9C45CE85325CD591DADBC4CA /* ElementXTests.swift in Sources */, 501304F26B52DF7024011B6C /* EmojiMartJSONLoaderTests.swift in Sources */, 25618589E0DE0F1E95FC7B5C /* EmojiProviderTests.swift in Sources */, + 71B62C48B8079D49F3FBC845 /* ExpiringTaskRunnerTests.swift in Sources */, CA45758F08DF42D41D8A4B29 /* FilePreviewViewModelTests.swift in Sources */, F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */, A23B8B27A1436A1049EEF68E /* InfoPlistReader.swift in Sources */, @@ -3688,6 +3695,7 @@ B5903E48CF43259836BF2DBF /* EncryptedRoomTimelineView.swift in Sources */, F78BAD28482A467287A9A5A3 /* EventBasedMessageTimelineItemProtocol.swift in Sources */, 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */, + 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */, 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */, 661A664C6EDF856B05519206 /* FilePreviewScreen.swift in Sources */, B5BD05558DC2C3091905E14A /* FilePreviewScreenCoordinator.swift in Sources */, @@ -3747,6 +3755,7 @@ B94368839BDB69172E28E245 /* MXLog.swift in Sources */, B66757D0254843162595B25D /* MXLogger.swift in Sources */, 67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */, + 8658F5034EAD7357CE7F9AC7 /* MatrixUserShareLink.swift in Sources */, BCC864190651B3A3CF51E4DF /* MediaFileHandleProxy.swift in Sources */, 208C19811613F9A10F8A7B75 /* MediaLoader.swift in Sources */, A2434D4DFB49A68E5CD0F53C /* MediaLoaderProtocol.swift in Sources */, @@ -3914,7 +3923,6 @@ E290C78E7F09F47FD2662986 /* Task.swift in Sources */, 1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */, 47305C0911C9E1AA774A4000 /* TemplateScreenCoordinator.swift in Sources */, - A0DF6EC32A17869500FC97FB /* MatrixUserShareLink.swift in Sources */, CCBEC2100CAF2EEBE9DB4156 /* TemplateScreenModels.swift in Sources */, 275EDE8849A2AC1D9309ED7C /* TemplateScreenViewModel.swift in Sources */, 2C4C750D0039AFABDF24236C /* TemplateScreenViewModelProtocol.swift in Sources */, diff --git a/ElementX/Sources/Other/ExpiringTaskRunner.swift b/ElementX/Sources/Other/ExpiringTaskRunner.swift new file mode 100644 index 000000000..e93a2f6aa --- /dev/null +++ b/ElementX/Sources/Other/ExpiringTaskRunner.swift @@ -0,0 +1,53 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +enum ExpiringTaskRunnerError: Error { + case timeout +} + +actor ExpiringTaskRunner { + private var continuation: CheckedContinuation? + + private var task: () async throws -> T + + init(_ task: @escaping () async throws -> T) { + self.task = task + } + + func run(timeout: Duration) async throws -> T { + try await withCheckedThrowingContinuation { + continuation = $0 + + Task { + try await Task.sleep(for: timeout) + continuation?.resume(with: .failure(ExpiringTaskRunnerError.timeout)) + continuation = nil + } + + Task { + do { + let result = try await task() + continuation?.resume(with: .success(result)) + } catch { + continuation?.resume(with: .failure(error)) + } + continuation = nil + } + } + } +} diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift index 7f010f1db..ecf646e12 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomCoordinator.swift @@ -19,12 +19,13 @@ import SwiftUI struct CreateRoomCoordinatorParameters { let userSession: UserSessionProtocol + weak var userIndicatorController: UserIndicatorControllerProtocol? let createRoomParameters: CurrentValuePublisher let selectedUsers: CurrentValuePublisher<[UserProfile], Never> } enum CreateRoomCoordinatorAction { - case createRoom + case openRoom(withIdentifier: String) case deselectUser(UserProfile) case updateDetails(CreateRoomFlowParameters) } @@ -42,6 +43,7 @@ final class CreateRoomCoordinator: CoordinatorProtocol { init(parameters: CreateRoomCoordinatorParameters) { self.parameters = parameters viewModel = CreateRoomViewModel(userSession: parameters.userSession, + userIndicatorController: parameters.userIndicatorController, createRoomParameters: parameters.createRoomParameters, selectedUsers: parameters.selectedUsers) } @@ -52,8 +54,8 @@ final class CreateRoomCoordinator: CoordinatorProtocol { switch action { case .deselectUser(let user): self.actionsSubject.send(.deselectUser(user)) - case .createRoom: - self.actionsSubject.send(.createRoom) + case .openRoom(let identifier): + self.actionsSubject.send(.openRoom(withIdentifier: identifier)) case .updateDetails(let details): self.actionsSubject.send(.updateDetails(details)) } diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift index 950d1b6fc..dfad7c312 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomModels.swift @@ -16,8 +16,13 @@ import Foundation +enum CreateRoomScreenErrorType: Error { + case failedCreatingRoom + case unknown +} + enum CreateRoomViewModelAction { - case createRoom + case openRoom(withIdentifier: String) case deselectUser(UserProfile) case updateDetails(CreateRoomFlowParameters) } @@ -35,6 +40,9 @@ struct CreateRoomViewStateBindings { var roomName: String var roomTopic: String var isRoomPrivate: Bool + + /// Information describing the currently displayed alert. + var alertInfo: AlertInfo? } enum CreateRoomViewAction { diff --git a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift index 03cdeeba4..4f49e7d3f 100644 --- a/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift +++ b/ElementX/Sources/Screens/CreateRoom/CreateRoomViewModel.swift @@ -20,17 +20,22 @@ import SwiftUI typealias CreateRoomViewModelType = StateStoreViewModel class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol { + private let userSession: UserSessionProtocol private var actionsSubject: PassthroughSubject = .init() private var createRoomParameters: CreateRoomFlowParameters - + private weak var userIndicatorController: UserIndicatorControllerProtocol? + var actions: AnyPublisher { actionsSubject.eraseToAnyPublisher() } init(userSession: UserSessionProtocol, + userIndicatorController: UserIndicatorControllerProtocol?, createRoomParameters: CurrentValuePublisher, selectedUsers: CurrentValuePublisher<[UserProfile], Never>) { let parameters = createRoomParameters.value + self.userSession = userSession + self.userIndicatorController = userIndicatorController self.createRoomParameters = parameters let bindings = CreateRoomViewStateBindings(roomName: parameters.name, roomTopic: parameters.topic, isRoomPrivate: parameters.isRoomPrivate) @@ -50,7 +55,9 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol override func process(viewAction: CreateRoomViewAction) { switch viewAction { case .createRoom: - actionsSubject.send(.createRoom) + Task { + await createRoom() + } case .deselectUser(let user): actionsSubject.send(.deselectUser(user)) } @@ -71,4 +78,49 @@ class CreateRoomViewModel: CreateRoomViewModelType, CreateRoomViewModelProtocol } .store(in: &cancellables) } + + private func displayError(_ type: ClientProxyError) { + switch type { + case .failedCreatingRoom: + state.bindings.alertInfo = AlertInfo(id: .failedCreatingRoom, + title: L10n.commonError, + message: L10n.screenStartChatErrorStartingChat) + case .failedSearchingUsers: + state.bindings.alertInfo = AlertInfo(id: .unknown) + default: + break + } + } + + private var clientProxy: ClientProxyProtocol { + userSession.clientProxy + } + + private func createRoom() async { + defer { + hideLoadingIndicator() + } + showLoadingIndicator() + switch await clientProxy.createRoom(with: createRoomParameters, userIDs: state.selectedUsers.map(\.userID)) { + case .success(let roomId): + actionsSubject.send(.openRoom(withIdentifier: roomId)) + case .failure(let failure): + displayError(failure) + } + } + + // MARK: Loading indicator + + private static let loadingIndicatorIdentifier = "CreateRoomLoading" + + private func showLoadingIndicator() { + userIndicatorController?.submitIndicator(UserIndicator(id: Self.loadingIndicatorIdentifier, + type: .modal, + title: L10n.commonLoading, + persistent: true)) + } + + private func hideLoadingIndicator() { + userIndicatorController?.retractIndicatorWithId(Self.loadingIndicatorIdentifier) + } } diff --git a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift index 24f721fd3..92fc135f8 100644 --- a/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift +++ b/ElementX/Sources/Screens/CreateRoom/View/CreateRoomScreen.swift @@ -166,14 +166,14 @@ struct CreateRoom_Previews: PreviewProvider { let parameters = CreateRoomFlowParameters() let selectedUsers: [UserProfile] = [.mockAlice, .mockBob, .mockCharlie] - return CreateRoomViewModel(userSession: userSession, createRoomParameters: .init(parameters), selectedUsers: .init(selectedUsers)) + return CreateRoomViewModel(userSession: userSession, userIndicatorController: nil, createRoomParameters: .init(parameters), selectedUsers: .init(selectedUsers)) }() static let emtpyViewModel = { let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"), mediaProvider: MockMediaProvider()) let parameters = CreateRoomFlowParameters() - return CreateRoomViewModel(userSession: userSession, createRoomParameters: .init(parameters), selectedUsers: .init([])) + return CreateRoomViewModel(userSession: userSession, userIndicatorController: nil, createRoomParameters: .init(parameters), selectedUsers: .init([])) }() static var previews: some View { diff --git a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift index 1a6c3c56e..c70657602 100644 --- a/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift +++ b/ElementX/Sources/Screens/InviteUsersScreen/View/InviteUsersScreen.swift @@ -21,32 +21,35 @@ struct InviteUsersScreen: View { @ObservedObject var context: InviteUsersScreenViewModel.Context var body: some View { - VStack { - if !context.viewState.selectedUsers.isEmpty { - selectedUsersSection + mainContent + .scrollContentBackground(.hidden) + .background(Color.element.formBackground.ignoresSafeArea()) + .navigationTitle(L10n.screenCreateRoomActionInvitePeople) + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .confirmationAction) { + nextButton + } } - searchContent - } - .scrollContentBackground(.hidden) - .background(Color.element.formBackground.ignoresSafeArea()) - .navigationTitle(L10n.screenCreateRoomActionInvitePeople) - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .confirmationAction) { - nextButton - } - } - .searchable(text: $context.searchQuery, placement: .navigationBarDrawer(displayMode: .always), prompt: L10n.commonSearchForSomeone) - .compoundSearchField() - .alert(item: $context.alertInfo) { $0.alert } + .searchable(text: $context.searchQuery, placement: .navigationBarDrawer(displayMode: .always), prompt: L10n.commonSearchForSomeone) + .compoundSearchField() + .alert(item: $context.alertInfo) { $0.alert } + .background(ViewFrameReader(frame: $frame)) } // MARK: - Private - /// The content shown in the form when a search query has been entered. - @ViewBuilder - private var searchContent: some View { + private var mainContent: some View { Form { + if !context.viewState.selectedUsers.isEmpty { + // this is a fix for having the carousel not clipped, and inside the form, so when the search is dismissed, it wont break the design + Section { + EmptyView() + } header: { + selectedUsersSection + .textCase(.none) + } + } if context.viewState.hasEmptySearchResults { noResultsContent } else { @@ -84,7 +87,9 @@ struct InviteUsersScreen: View { .formSectionStyle() } + @State private var frame: CGRect = .zero @ScaledMetric private var cellWidth: CGFloat = 64 + private var selectedUsersSection: some View { ScrollView(.horizontal, showsIndicators: false) { ScrollViewReader { scrollView in @@ -105,6 +110,7 @@ struct InviteUsersScreen: View { .padding(.horizontal, 18) } } + .frame(width: frame.width) } private var nextButton: some View { diff --git a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift index f464f1bc9..8b42a4e3b 100644 --- a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift +++ b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenCoordinator.swift @@ -107,6 +107,7 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { private func openCreateRoomScreen() { let createParameters = CreateRoomCoordinatorParameters(userSession: parameters.userSession, + userIndicatorController: parameters.userIndicatorController, createRoomParameters: createRoomParametersPublisher, selectedUsers: selectedUsersPublisher) let coordinator = CreateRoomCoordinator(parameters: createParameters) @@ -116,8 +117,8 @@ final class StartChatScreenCoordinator: CoordinatorProtocol { self?.toggleUser(user) case .updateDetails(let details): self?.createRoomParameters.send(details) - case .createRoom: - break + case .openRoom(let identifier): + self?.actionsSubject.send(.openRoom(withIdentifier: identifier)) } } .store(in: &cancellables) diff --git a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift index 5eab2775c..80296b05e 100644 --- a/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift +++ b/ElementX/Sources/Screens/StartChatScreen/StartChatScreenViewModel.swift @@ -127,10 +127,11 @@ class StartChatScreenViewModel: StartChatScreenViewModelType, StartChatScreenVie } private func createDirectRoom(with user: UserProfile) async { + defer { + hideLoadingIndicator() + } showLoadingIndicator() - let result = await clientProxy.createDirectRoom(with: user.userID) - hideLoadingIndicator() - switch result { + switch await clientProxy.createDirectRoom(with: user.userID, expectedRoomName: user.displayName) { case .success(let roomId): actionsSubject.send(.openRoom(withIdentifier: roomId)) case .failure(let failure): diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 407d2320c..22e623df8 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -192,9 +192,9 @@ class ClientProxy: ClientProxyProtocol { } } } - - func createDirectRoom(with userID: String) async -> Result { - await Task.dispatch(on: clientQueue) { + + func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result { + let result: Result = await Task.dispatch(on: clientQueue) { do { let parameters = CreateRoomParameters(name: nil, topic: nil, isEncrypted: true, isDirect: true, visibility: .private, preset: .trustedPrivateChat, invite: [userID], avatar: nil) let result = try self.client.createRoom(request: parameters) @@ -203,6 +203,52 @@ class ClientProxy: ClientProxyProtocol { return .failure(.failedCreatingRoom) } } + + return await waitForRoomSummary(with: result, name: expectedRoomName) + } + + func createRoom(with parameters: CreateRoomFlowParameters, userIDs: [String]) async -> Result { + let result: Result = await Task.dispatch(on: clientQueue) { + do { + let parameters = CreateRoomParameters(name: parameters.name, + topic: parameters.topic, + isEncrypted: parameters.isRoomPrivate, + isDirect: false, + visibility: parameters.isRoomPrivate ? .private : .public, + preset: parameters.isRoomPrivate ? .privateChat : .publicChat, + invite: userIDs, + avatar: nil) + let roomId = try self.client.createRoom(request: parameters) + return .success(roomId) + } catch { + return .failure(.failedCreatingRoom) + } + } + + return await waitForRoomSummary(with: result, name: parameters.name) + } + + /// Await the room to be available in the room summary list + /// - Parameter result: the result of a room creation Task with the roomId + private func waitForRoomSummary(with result: Result, name: String?) async -> Result { + guard case .success(let roomId) = result else { return result } + let runner = ExpiringTaskRunner { [weak self] in + guard let roomLists = self?.allRoomsSummaryProvider?.roomListPublisher.values else { + return + } + // for every list of summaries, we check if we have a room summary with matching ID and name (if present) + for await roomList in roomLists { + guard let summary = roomList.first(where: { $0.id == roomId }) else { continue } + guard let name else { break } + if summary.name == name { + break + } + } + } + + // we want to ignore the timeout error, and return the .success case because the room it was properly created already, we are only waiting for it to appear + try? await runner.run(timeout: .seconds(10)) + return result } func roomForIdentifier(_ identifier: String) async -> RoomProxyProtocol? { diff --git a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift index 795f07420..3b4f8f542 100644 --- a/ElementX/Sources/Services/Client/ClientProxyProtocol.swift +++ b/ElementX/Sources/Services/Client/ClientProxyProtocol.swift @@ -94,7 +94,9 @@ protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol { func directRoomForUserID(_ userID: String) async -> Result - func createDirectRoom(with userID: String) async -> Result + func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result + + func createRoom(with parameters: CreateRoomFlowParameters, userIDs: [String]) async -> Result func roomForIdentifier(_ identifier: String) async -> RoomProxyProtocol? diff --git a/ElementX/Sources/Services/Client/MockClientProxy.swift b/ElementX/Sources/Services/Client/MockClientProxy.swift index 9a8a5ffd7..ac70ec08a 100644 --- a/ElementX/Sources/Services/Client/MockClientProxy.swift +++ b/ElementX/Sources/Services/Client/MockClientProxy.swift @@ -53,7 +53,11 @@ class MockClientProxy: ClientProxyProtocol { .failure(.failedRetrievingDirectRoom) } - func createDirectRoom(with userID: String) async -> Result { + func createDirectRoom(with userID: String, expectedRoomName: String?) async -> Result { + .failure(.failedCreatingRoom) + } + + func createRoom(with parameters: CreateRoomFlowParameters, userIDs: [String]) async -> Result { .failure(.failedCreatingRoom) } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift index 083ac7c28..50bd0aa28 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProviderProtocol.swift @@ -39,6 +39,15 @@ enum RoomSummary: CustomStringConvertible { } } + var name: String? { + switch self { + case .empty: + return nil + case .invalidated(let details), .filled(let details): + return details.name + } + } + var description: String { switch self { case .empty: diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 78e172ee4..7a43f8e30 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -473,7 +473,7 @@ class MockScreen: Identifiable { let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()) let createRoomParameters = CreateRoomFlowParameters() let selectedUsers: [UserProfile] = [.mockAlice, .mockBob, .mockCharlie] - let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, createRoomParameters: .init(createRoomParameters), selectedUsers: .init(selectedUsers)) + let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: MockUserIndicatorController(), createRoomParameters: .init(createRoomParameters), selectedUsers: .init(selectedUsers)) let coordinator = CreateRoomCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator @@ -482,7 +482,7 @@ class MockScreen: Identifiable { let clientProxy = MockClientProxy(userID: "@mock:client.com") let mockUserSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()) let createRoomParameters = CreateRoomFlowParameters() - let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, createRoomParameters: .init(createRoomParameters), selectedUsers: .init([])) + let parameters = CreateRoomCoordinatorParameters(userSession: mockUserSession, userIndicatorController: MockUserIndicatorController(), createRoomParameters: .init(createRoomParameters), selectedUsers: .init([])) let coordinator = CreateRoomCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) return navigationStackCoordinator diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers-1.png index 9e4095326..c11140c09 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27f23d7385277433b757e7513bea0c6349c2df2cc1381fdd7ac562d4ae8a2679 -size 108566 +oid sha256:8246c89a96e3556b237df058f87530c4a691d15977d7071eacbc2a56e5c5cf79 +size 108593 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers.png index 05b4893f9..32d7dbb7a 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPad-9th-generation.inviteUsers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f627692fab53a50a5cada0b7267d691d17d4dfe18973953ca3f5362f65902dde -size 98917 +oid sha256:0126c1cec30da2fb2dd10aa03713ae73cc65e19a14482ce2bd3f6ce51f74ab9c +size 98972 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers-1.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers-1.png index 78ba8c4fe..bf11a4146 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers-1.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b488f2fb2041325f0776e027ea202655adba51a0e12bdaf5ff07d8f4484f7b31 -size 133019 +oid sha256:058b2bc5f9767f51daf3bb1b072f280c5be8773bdfbac059123d74846d408149 +size 132527 diff --git a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers.png b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers.png index 0478e8798..372c1a9f2 100644 --- a/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers.png +++ b/UITests/Sources/__Snapshots__/Application/en-GB-iPhone-14.inviteUsers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bae5f65a07ef1ba6bc6b573b82f5cda1c4d565ed28c9663ea9a6fd9ffda1c87c -size 113345 +oid sha256:4254f22da797a44cbb7dda8c696315c04e826a72a6569672327597a9b02ecd57 +size 115498 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers-1.png index 54c30668b..a91aee4a8 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e482ce2b580347a2f73657caf4aba77f330097b5edb792c22e75b0bfd4445af -size 111217 +oid sha256:0a9ed0d1fc9b8d0d7646740015a617376c6d1fd0052d8036f2336adf4db2c688 +size 111420 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers.png index 9a5aef376..56140e221 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPad-9th-generation.inviteUsers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc592dbfcaee93de685ad027de94e6426e16fa523293912c7f3f52b4afd9d196 -size 102328 +oid sha256:f2ceb1f749349fbf41e5fabb7dfd3e7daf5adb47dfb35810bd0f9ed01c8bdc98 +size 102368 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers-1.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers-1.png index a709ea8c0..fb886cea0 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers-1.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers-1.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4a44592fe680d00de1cf31d1dd0a6c115131f02849b6a75360e81b736edb170d -size 137142 +oid sha256:103cf9d17c3b1f7caceb82d725d13c099f51a9f329453e3690bf9c951fe7daab +size 136625 diff --git a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers.png b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers.png index 348d741e4..b7ed7ce42 100644 --- a/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers.png +++ b/UITests/Sources/__Snapshots__/Application/pseudo-iPhone-14.inviteUsers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69e5995f2ce7e53a5d8912bc95562ca7029c1ff52ce7ec2e439a6b78abe28d3a -size 117528 +oid sha256:3def893c8b3107f5382c260d5643f2229e23a738b73c94ec4f55d63e1353ca1b +size 117552 diff --git a/UnitTests/Sources/CreateRoomViewModelTests.swift b/UnitTests/Sources/CreateRoomViewModelTests.swift index 5e760cc6d..d56904c3e 100644 --- a/UnitTests/Sources/CreateRoomViewModelTests.swift +++ b/UnitTests/Sources/CreateRoomViewModelTests.swift @@ -37,7 +37,7 @@ class CreateRoomScreenViewModelTests: XCTestCase { userSession = MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider()) let parameters = CreateRoomFlowParameters() usersSubject.send([.mockAlice, .mockBob, .mockCharlie]) - let viewModel = CreateRoomViewModel(userSession: userSession, createRoomParameters: .init(parameters), selectedUsers: usersSubject.asCurrentValuePublisher()) + let viewModel = CreateRoomViewModel(userSession: userSession, userIndicatorController: nil, createRoomParameters: .init(parameters), selectedUsers: usersSubject.asCurrentValuePublisher()) self.viewModel = viewModel viewModel.actions.sink { [weak self] action in diff --git a/UnitTests/Sources/ExpiringTaskRunnerTests.swift b/UnitTests/Sources/ExpiringTaskRunnerTests.swift new file mode 100644 index 000000000..b41c5c7d3 --- /dev/null +++ b/UnitTests/Sources/ExpiringTaskRunnerTests.swift @@ -0,0 +1,63 @@ +// +// Copyright 2023 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +import XCTest + +@testable import ElementX + +class ExpiringTaskRunnerTests: XCTestCase { + enum ExpiringTaskTestError: Error { + case failed + } + + func testSuccedingTask() async { + let runner = ExpiringTaskRunner { + try? await Task.sleep(for: .milliseconds(300)) + return true + } + + let result = try? await runner.run(timeout: .seconds(1)) + XCTAssertEqual(result, true) + } + + func testFailingTask() async { + let runner: ExpiringTaskRunner> = ExpiringTaskRunner { + try? await Task.sleep(for: .milliseconds(300)) + return .failure(.failed) + } + + do { + let result = try await runner.run(timeout: .seconds(1)) + } catch { + XCTAssertEqual(error as? ExpiringTaskTestError, ExpiringTaskTestError.failed) + } + } + + func testTimeoutTask() async { + let runner = ExpiringTaskRunner { + try? await Task.sleep(for: .milliseconds(300)) + return true + } + + do { + let result = try await runner.run(timeout: .milliseconds(100)) + } catch { + XCTAssertEqual(error as? ExpiringTaskRunnerError, ExpiringTaskRunnerError.timeout) + } + } +} diff --git a/changelog.d/925.feature b/changelog.d/925.feature new file mode 100644 index 000000000..2b7af1f40 --- /dev/null +++ b/changelog.d/925.feature @@ -0,0 +1 @@ +Add creation of a room, fetching the summary, so the room will be ready to be presented. \ No newline at end of file