Share extension (#3506)

* Setup simple share extension

* Switch the app url scheme to be the full bundle identifier

* Setup a share extension that show a SwiftUI view, uses rust tracing and redirects to the hosting aplication

* Move media as json through the custom scheme into the main app and deep link into the media upload preview screen

* Fix message forwarding and global search screen room summary provider filtering.

* Tweak the message forwarding and global search screen designs.

* Add a room selection screen to use after receiving a share request from the share extension

* Fix share extension entitlements

* Share the temporary directory between the main app and the extensions; rename the caches one.

* Remove the no longer needed notification avatar flipping fix.

* Extract the placeholder avatar image generator from the NSE

* Nest `AvatarSize` within the new `Avatars` enum

* Donate an `INSendMessageIntent` to the system every time we send a message so they appear as share suggestions

* Support suggestions in the share extension itself

* Improve sharing animations and fix presentation when room already on the stack

* Clear all routes when sharing without a preselected room.

* Fix broken unit tests

* Various initial tweaks following code review.

* Correctly clean up and dismiss the share extension for all paths.

* Move the share extension path to a constants enum

* Rename UserSessionFlowCoordinator specific share extension states and events

* Add UserSession and Room flow coordinator share route tests

* Tweak the share extension logic.
This commit is contained in:
Stefan Ceriu
2024-11-13 14:02:47 +02:00
committed by GitHub
parent 3a600a94d5
commit b122b02bee
69 changed files with 1583 additions and 272 deletions
+274 -10
View File
@@ -22,6 +22,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C070FD43DC6BF4E50217965A /* LocalizationTests.swift */; }; 0033481EE363E4914295F188 /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C070FD43DC6BF4E50217965A /* LocalizationTests.swift */; };
00C3023B6DF55024D8876B76 /* ShareExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3D8BEEFCA07BEA43F4F4BF77 /* ShareExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
01681E8B20AD6F0D237F2DC1 /* IdentityConfirmedScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C6624240FFD32B7F0834229 /* IdentityConfirmedScreenViewModel.swift */; }; 01681E8B20AD6F0D237F2DC1 /* IdentityConfirmedScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C6624240FFD32B7F0834229 /* IdentityConfirmedScreenViewModel.swift */; };
0180C44B997EDA8D21F883AC /* RoomNotificationSettingsCustomSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B746EFA112532A7B701FB914 /* RoomNotificationSettingsCustomSectionView.swift */; }; 0180C44B997EDA8D21F883AC /* RoomNotificationSettingsCustomSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B746EFA112532A7B701FB914 /* RoomNotificationSettingsCustomSectionView.swift */; };
01B63F1A04A276B39AC17014 /* CallInviteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9A3D3CFA199FA7897364547 /* CallInviteRoomTimelineItem.swift */; }; 01B63F1A04A276B39AC17014 /* CallInviteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9A3D3CFA199FA7897364547 /* CallInviteRoomTimelineItem.swift */; };
@@ -31,6 +32,7 @@
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; }; 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */; };
037006FB6DF1374F94E4058D /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */; }; 037006FB6DF1374F94E4058D /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */; };
038AB2E86960FD240231D4C2 /* GeneratedPreviewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A2E4BD7C0CAD25EF924A4C /* GeneratedPreviewTests.swift */; }; 038AB2E86960FD240231D4C2 /* GeneratedPreviewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A2E4BD7C0CAD25EF924A4C /* GeneratedPreviewTests.swift */; };
03BD83E8BDD23AE059802E0D /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955336CBD5ED73C792D1F580 /* EncryptionAuthenticity.swift */; }; 03CDCA6243F89B194E3FAD17 /* EncryptionAuthenticity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 955336CBD5ED73C792D1F580 /* EncryptionAuthenticity.swift */; };
0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */; }; 0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */; };
044DD8F80231BC30570F7965 /* UserDiscoveryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAD845E53B0C8B5E0812C2 /* UserDiscoveryService.swift */; }; 044DD8F80231BC30570F7965 /* UserDiscoveryService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65AAD845E53B0C8B5E0812C2 /* UserDiscoveryService.swift */; };
@@ -39,7 +41,10 @@
059173B3C77056C406906B6D /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = D4DA544B2520BFA65D6DB4BB /* target.yml */; }; 059173B3C77056C406906B6D /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = D4DA544B2520BFA65D6DB4BB /* target.yml */; };
05BAB510CBC2ED35C154ADD0 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFD012C3A9F5EF276DDD4AA /* AnalyticsPromptScreenViewModelProtocol.swift */; }; 05BAB510CBC2ED35C154ADD0 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFD012C3A9F5EF276DDD4AA /* AnalyticsPromptScreenViewModelProtocol.swift */; };
05EC896A4B9AF4A56670C0BB /* SessionVerificationUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */; }; 05EC896A4B9AF4A56670C0BB /* SessionVerificationUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */; };
05FF0CD80EDAB3A7C0D4700A /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
0638CBDE3098B1C3F23AFCFA /* MXLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111B698739E3410E2CDB7144 /* MXLog.swift */; };
066A1E9B94723EE9F3038044 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; }; 066A1E9B94723EE9F3038044 /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; };
069358C2C825A19DE6CB127E /* TracingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */; };
06B31F84CE52A7A7C271267C /* SecureBackupRecoveryKeyScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */; }; 06B31F84CE52A7A7C271267C /* SecureBackupRecoveryKeyScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */; };
06B55882911B4BF5B14E9851 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 227AC5D71A4CE43512062243 /* URL.swift */; }; 06B55882911B4BF5B14E9851 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 227AC5D71A4CE43512062243 /* URL.swift */; };
06D3942496E9E0E655F14D21 /* NotificationManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */; }; 06D3942496E9E0E655F14D21 /* NotificationManagerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A057F2FDC14866C3026A89A4 /* NotificationManagerProtocol.swift */; };
@@ -109,6 +114,7 @@
1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4549FCB53F43DB0B278374BC /* TemplateScreen.swift */; }; 1555A7643D85187D4851040C /* TemplateScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4549FCB53F43DB0B278374BC /* TemplateScreen.swift */; };
1583E2D766E4485FF91662FC /* PermalinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3EB5B1848CF4F64E63C6B7 /* PermalinkTests.swift */; }; 1583E2D766E4485FF91662FC /* PermalinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3EB5B1848CF4F64E63C6B7 /* PermalinkTests.swift */; };
15913A5B07118C1268A840E4 /* RoomSummaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */; }; 15913A5B07118C1268A840E4 /* RoomSummaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */; };
1621BF6316FFFEF5AE067C77 /* Avatars.swift in Sources */ = {isa = PBXBuildFile; fileRef = C142248014E08E885E323E56 /* Avatars.swift */; };
1653275750CE11F5CE94DDFD /* ReadReceiptsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8063E65441E771200108C558 /* ReadReceiptsSummaryView.swift */; }; 1653275750CE11F5CE94DDFD /* ReadReceiptsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8063E65441E771200108C558 /* ReadReceiptsSummaryView.swift */; };
167D00CAA13FAFB822298021 /* MediaProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */; }; 167D00CAA13FAFB822298021 /* MediaProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */; };
16CBD087038DE3815CDA512C /* PollMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D38391154120264910D19528 /* PollMock.swift */; }; 16CBD087038DE3815CDA512C /* PollMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = D38391154120264910D19528 /* PollMock.swift */; };
@@ -119,12 +125,14 @@
17BC15DA08A52587466698C5 /* RoomMessageEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E815FF3CC5E5A355E3A25E /* RoomMessageEventStringBuilder.swift */; }; 17BC15DA08A52587466698C5 /* RoomMessageEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E815FF3CC5E5A355E3A25E /* RoomMessageEventStringBuilder.swift */; };
18867F4F1C8991EEC56EA932 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; }; 18867F4F1C8991EEC56EA932 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; };
18E3786918486D4C9726BC84 /* FormButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89FBFC09F9DAFF1E4BA97849 /* FormButtonStyles.swift */; }; 18E3786918486D4C9726BC84 /* FormButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89FBFC09F9DAFF1E4BA97849 /* FormButtonStyles.swift */; };
18FDE4ED6D83B0771452B43D /* RoomSelectionScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F104596B0620CEFE5DFD31B1 /* RoomSelectionScreenCoordinator.swift */; };
192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */; }; 192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */; };
1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */; }; 1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */; };
197441F1EF23A5DABACCA79F /* StickerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5338450E6783A576B5C16DD /* StickerRoomTimelineView.swift */; }; 197441F1EF23A5DABACCA79F /* StickerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5338450E6783A576B5C16DD /* StickerRoomTimelineView.swift */; };
19DED23340D0855B59693ED2 /* VoiceMessageRecorderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45C9EAA86423D7D3126DE4F /* VoiceMessageRecorderProtocol.swift */; }; 19DED23340D0855B59693ED2 /* VoiceMessageRecorderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45C9EAA86423D7D3126DE4F /* VoiceMessageRecorderProtocol.swift */; };
19DF5600A7F547B22DD7872A /* CompletionSuggestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A12D3D8138F1B71AFA7C858 /* CompletionSuggestionService.swift */; }; 19DF5600A7F547B22DD7872A /* CompletionSuggestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A12D3D8138F1B71AFA7C858 /* CompletionSuggestionService.swift */; };
19FE025AE9BA2959B6589B0D /* RoomMemberDetailsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CC575D1895FA62591451A93 /* RoomMemberDetailsScreen.swift */; }; 19FE025AE9BA2959B6589B0D /* RoomMemberDetailsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CC575D1895FA62591451A93 /* RoomMemberDetailsScreen.swift */; };
1A3783005E6945F8583AF997 /* NSItemProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72EFC8C634469F9262659C7 /* NSItemProvider.swift */; };
1A3B073568D1DC8F76F1F3A0 /* UserProfileScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EE69982BBA18C6D51AD08E /* UserProfileScreen.swift */; }; 1A3B073568D1DC8F76F1F3A0 /* UserProfileScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23EE69982BBA18C6D51AD08E /* UserProfileScreen.swift */; };
1A70A2199394B5EC660934A5 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = A678E40E917620059695F067 /* MatrixRustSDK */; }; 1A70A2199394B5EC660934A5 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = A678E40E917620059695F067 /* MatrixRustSDK */; };
1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */; }; 1A83DD22F3E6F76B13B6E2F9 /* VideoRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C8616254EE40CA8BA5E9BC2 /* VideoRoomTimelineItemContent.swift */; };
@@ -176,12 +184,14 @@
25C4C1100B6EA79F5CC7CBB5 /* AppLockSetupPINScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */; }; 25C4C1100B6EA79F5CC7CBB5 /* AppLockSetupPINScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */; };
260FFC1475EE94F641C3F3F9 /* PollFormScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40F1985065500F0E7F61A27 /* PollFormScreenViewModelProtocol.swift */; }; 260FFC1475EE94F641C3F3F9 /* PollFormScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40F1985065500F0E7F61A27 /* PollFormScreenViewModelProtocol.swift */; };
261261778DEFAEFC042B875E /* JoinedRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */; }; 261261778DEFAEFC042B875E /* JoinedRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07C6B0B087FE6601C3F77816 /* JoinedRoomProxy.swift */; };
26252AA9AED64010788F4C26 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A3E8741D199CD1A37F4CBF /* UIView.swift */; };
2689D22EF1D10D22B0A4DAEA /* NotificationContentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */; }; 2689D22EF1D10D22B0A4DAEA /* NotificationContentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */; };
273AB64B9A26B61C51858867 /* AsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73A07BAEDD74C48795A996A /* AsyncSequence.swift */; }; 273AB64B9A26B61C51858867 /* AsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73A07BAEDD74C48795A996A /* AsyncSequence.swift */; };
274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97CE98208321C4D66E363612 /* ShimmerModifier.swift */; }; 274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97CE98208321C4D66E363612 /* ShimmerModifier.swift */; };
275EDE8849A2AC1D9309ED7C /* TemplateScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43456E73F8A2D52B69B9FB9 /* TemplateScreenViewModel.swift */; }; 275EDE8849A2AC1D9309ED7C /* TemplateScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43456E73F8A2D52B69B9FB9 /* TemplateScreenViewModel.swift */; };
2797C9D9BA642370F1C85D78 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; }; 2797C9D9BA642370F1C85D78 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; };
27F015B0D5436633B5B3C8C3 /* SecureBackupRecoveryKeyScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7061BE2C0BF427C38AEDEF5E /* SecureBackupRecoveryKeyScreenViewModel.swift */; }; 27F015B0D5436633B5B3C8C3 /* SecureBackupRecoveryKeyScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7061BE2C0BF427C38AEDEF5E /* SecureBackupRecoveryKeyScreenViewModel.swift */; };
27FEF0F40750465195C9D6D6 /* RoomSelectionScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B9D191A81FFB0C72CE73E77 /* RoomSelectionScreenModels.swift */; };
2814E7075BF3A5C0CCBC9F90 /* RoomDirectorySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AF32E4136FD6F159D86C2C /* RoomDirectorySearchView.swift */; }; 2814E7075BF3A5C0CCBC9F90 /* RoomDirectorySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AF32E4136FD6F159D86C2C /* RoomDirectorySearchView.swift */; };
281BED345D59A9A6A99E9D98 /* UNNotificationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */; }; 281BED345D59A9A6A99E9D98 /* UNNotificationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE148A4FFEE853C5A281500C /* UNNotificationContent.swift */; };
282A5F3375DDC774AE09B0C3 /* TracingConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1734A445A58ED855B977A0A8 /* TracingConfigurationTests.swift */; }; 282A5F3375DDC774AE09B0C3 /* TracingConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1734A445A58ED855B977A0A8 /* TracingConfigurationTests.swift */; };
@@ -214,6 +224,7 @@
2E8C6672D0EE7D5B1BEDB8E2 /* ServerConfirmationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */; }; 2E8C6672D0EE7D5B1BEDB8E2 /* ServerConfirmationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7478623CECC9438014244BA /* ServerConfirmationScreen.swift */; };
2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */; }; 2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B10423B9102086A2D9BFCBA /* EventTimelineItem.swift */; };
2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086B997409328F091EBA43CE /* RoomScreenUITests.swift */; }; 2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086B997409328F091EBA43CE /* RoomScreenUITests.swift */; };
2F2906AE9BC3D0E79A6F98F8 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6E89E530A8E92EC44301CA1 /* Bundle.swift */; };
2F6207CB5C4715FE313B1E95 /* TimelineViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6509708F54FC883604DFDC95 /* TimelineViewModelTests.swift */; }; 2F6207CB5C4715FE313B1E95 /* TimelineViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6509708F54FC883604DFDC95 /* TimelineViewModelTests.swift */; };
2F623DA1122140A987B34D08 /* NotificationSettingsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */; }; 2F623DA1122140A987B34D08 /* NotificationSettingsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.swift */; };
2F94054F50E312AF30BE07F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B21E611DADDEF00307E7AC /* String.swift */; }; 2F94054F50E312AF30BE07F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B21E611DADDEF00307E7AC /* String.swift */; };
@@ -243,6 +254,7 @@
355B11D08CE0CEF97A813236 /* AppRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A9E3FBE8A66B5A17AD7F74 /* AppRoutes.swift */; }; 355B11D08CE0CEF97A813236 /* AppRoutes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A9E3FBE8A66B5A17AD7F74 /* AppRoutes.swift */; };
3582056513A384F110EC8274 /* MediaPlayerProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7A2C4A3A74F0D2FFE9356A /* MediaPlayerProviderTests.swift */; }; 3582056513A384F110EC8274 /* MediaPlayerProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7A2C4A3A74F0D2FFE9356A /* MediaPlayerProviderTests.swift */; };
35E975CFDA60E05362A7CF79 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 1222DB76B917EB8A55365BA5 /* target.yml */; }; 35E975CFDA60E05362A7CF79 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 1222DB76B917EB8A55365BA5 /* target.yml */; };
36206F74DDEBF9BEAF6A6A1F /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
366D5BFE52CB79E804C7D095 /* CallScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD9547E47C58930E2CE8306 /* CallScreenViewModelTests.swift */; }; 366D5BFE52CB79E804C7D095 /* CallScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAD9547E47C58930E2CE8306 /* CallScreenViewModelTests.swift */; };
36926D795D6D19177C7812F8 /* EncryptionResetPasswordScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6935A55AB3B0C94BC566DD6 /* EncryptionResetPasswordScreenCoordinator.swift */; }; 36926D795D6D19177C7812F8 /* EncryptionResetPasswordScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6935A55AB3B0C94BC566DD6 /* EncryptionResetPasswordScreenCoordinator.swift */; };
369BF960E52BBEE61F8A5BD1 /* BlockedUsersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */; }; 369BF960E52BBEE61F8A5BD1 /* BlockedUsersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */; };
@@ -264,6 +276,7 @@
3982C505960006B341CFD0C6 /* UserDetailsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D0EA07BD545CC9F234DB8D /* UserDetailsEditScreenModels.swift */; }; 3982C505960006B341CFD0C6 /* UserDetailsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27D0EA07BD545CC9F234DB8D /* UserDetailsEditScreenModels.swift */; };
3982E60F9C126437D5E488A3 /* PillContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A6314FDC51DA25712D9A81 /* PillContextTests.swift */; }; 3982E60F9C126437D5E488A3 /* PillContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A6314FDC51DA25712D9A81 /* PillContextTests.swift */; };
39A987B3E41B976D1DF944C6 /* CallScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A63A59BFDDC494B1C20119 /* CallScreenViewModel.swift */; }; 39A987B3E41B976D1DF944C6 /* CallScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A63A59BFDDC494B1C20119 /* CallScreenViewModel.swift */; };
39DFC4B9EB6A8757210BDEC6 /* RoomSelectionScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DD2A058F3566FEEBA1D11B3 /* RoomSelectionScreenViewModelProtocol.swift */; };
3A08584ECDD4A4541DBF21F8 /* EmojiLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */; }; 3A08584ECDD4A4541DBF21F8 /* EmojiLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */; };
3A164187907DA43B7858F9EC /* CompletionSuggestionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */; }; 3A164187907DA43B7858F9EC /* CompletionSuggestionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */; };
3A64A93A651A3CB8774ADE8E /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = BA93CD75CCE486660C9040BD /* Collections */; }; 3A64A93A651A3CB8774ADE8E /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = BA93CD75CCE486660C9040BD /* Collections */; };
@@ -360,6 +373,7 @@
4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33035418BB35754232985871 /* TimelineReadReceiptsView.swift */; }; 4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33035418BB35754232985871 /* TimelineReadReceiptsView.swift */; };
4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; }; 4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.swift */; };
4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; }; 4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; };
4E22086585CB3B35FEEFBBB9 /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35FA991289149D31F4286747 /* UserPreference.swift */; };
4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */; }; 4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */; };
4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.swift */; }; 4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.swift */; };
4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */; }; 4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */; };
@@ -386,11 +400,11 @@
53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */; }; 53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */; };
53DEF39F0C4DE02E3FC56D91 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; }; 53DEF39F0C4DE02E3FC56D91 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; };
53F1196F9C69512306A2693F /* TextRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */; }; 53F1196F9C69512306A2693F /* TextRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C19F54A0C4FC9AB7ABD583 /* TextRoomTimelineItemContent.swift */; };
5455147CAC63F71E48F7D699 /* NSELogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D455BC2423D911A62ACFB2 /* NSELogger.swift */; };
54AE8860D668AFD96E7E177B /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 54AE8860D668AFD96E7E177B /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; }; 54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; };
5518DA4A6C9B4FC4B497EA9A /* LogViewerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B795AAAB7B8747FE2FF311 /* LogViewerScreenModels.swift */; }; 5518DA4A6C9B4FC4B497EA9A /* LogViewerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B795AAAB7B8747FE2FF311 /* LogViewerScreenModels.swift */; };
558E2673B04FDD06A1A12DD3 /* LogViewerScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7463464054DDF194C54F0B04 /* LogViewerScreenViewModelProtocol.swift */; }; 558E2673B04FDD06A1A12DD3 /* LogViewerScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7463464054DDF194C54F0B04 /* LogViewerScreenViewModelProtocol.swift */; };
558F37B1A8F2C4CC9B1ACEDA /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = 3262F08E1C3483C22A7A319F /* Compound */; };
55CDD3968D95D1A820B5491E /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; }; 55CDD3968D95D1A820B5491E /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; };
55D18AA4F4A2257642EBDB94 /* GlobalSearchScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38354164AF59C5006CD05878 /* GlobalSearchScreenViewModel.swift */; }; 55D18AA4F4A2257642EBDB94 /* GlobalSearchScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38354164AF59C5006CD05878 /* GlobalSearchScreenViewModel.swift */; };
562EFB9AB62B38830D9AA778 /* TimelineMediaFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */; }; 562EFB9AB62B38830D9AA778 /* TimelineMediaFrame.swift in Sources */ = {isa = PBXBuildFile; fileRef = 933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */; };
@@ -409,6 +423,8 @@
5992EF10AA157EBD97D88910 /* AudioRecorderState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6569593FA36B22259E806A67 /* AudioRecorderState.swift */; }; 5992EF10AA157EBD97D88910 /* AudioRecorderState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6569593FA36B22259E806A67 /* AudioRecorderState.swift */; };
59C41313AED7566C3AC51163 /* RoomSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29A953B6C0C431DBF4DD00B4 /* RoomSummary.swift */; }; 59C41313AED7566C3AC51163 /* RoomSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29A953B6C0C431DBF4DD00B4 /* RoomSummary.swift */; };
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2245243369B99216C7D84E /* ImageCache.swift */; }; 59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2245243369B99216C7D84E /* ImageCache.swift */; };
5AA81A4E2D40A32A9E7F71F2 /* ShareExtensionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3ADF21BE301D0DA48F2A7E /* ShareExtensionView.swift */; };
5AC5CD6D893073EE4D9A277E /* ShareExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D27299A36536DBF91AE8FA6 /* ShareExtensionViewController.swift */; };
5AE6404C4FD4848ACCFF9EDC /* SecureBackupLogoutConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */; }; 5AE6404C4FD4848ACCFF9EDC /* SecureBackupLogoutConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */; };
5B6E5AD224509E6C0B520D6E /* RoomMemberDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.swift */; }; 5B6E5AD224509E6C0B520D6E /* RoomMemberDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.swift */; };
5B7D24A318AFF75AD611A026 /* RoomDirectorySearchScreenScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6BFF453838CF6C3982C5A3 /* RoomDirectorySearchScreenScreenViewModelTests.swift */; }; 5B7D24A318AFF75AD611A026 /* RoomDirectorySearchScreenScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6BFF453838CF6C3982C5A3 /* RoomDirectorySearchScreenScreenViewModelTests.swift */; };
@@ -421,6 +437,7 @@
5D53AE9342A4C06B704247ED /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; }; 5D53AE9342A4C06B704247ED /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
5D56CE09743C6B90C21B04C2 /* RoomMembersListScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E0929CEFA356090BE5FB8 /* RoomMembersListScreenViewModelTests.swift */; }; 5D56CE09743C6B90C21B04C2 /* RoomMembersListScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E0929CEFA356090BE5FB8 /* RoomMembersListScreenViewModelTests.swift */; };
5D70FAE4D2BF4553AFFFFE41 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; }; 5D70FAE4D2BF4553AFFFFE41 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
5D99F63CC88BB29383019FC6 /* ShareExtensionModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */; };
5DD0EF30070DC0A82C5CCD33 /* RoomMembersListManageMemberSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */; }; 5DD0EF30070DC0A82C5CCD33 /* RoomMembersListManageMemberSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */; };
5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */; }; 5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */; };
5EE1D4E316D66943E97FDCF2 /* BloomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BEB970F500BFB248443FA1 /* BloomView.swift */; }; 5EE1D4E316D66943E97FDCF2 /* BloomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BEB970F500BFB248443FA1 /* BloomView.swift */; };
@@ -463,6 +480,8 @@
66357ECB73B1290E5490A012 /* WebRegistrationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F418426410F3823F3EB0828 /* WebRegistrationScreenViewModelProtocol.swift */; }; 66357ECB73B1290E5490A012 /* WebRegistrationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F418426410F3823F3EB0828 /* WebRegistrationScreenViewModelProtocol.swift */; };
663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.swift */; }; 663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.swift */; };
6681D6D3ADF69EBD2625F29A /* KnockedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */; }; 6681D6D3ADF69EBD2625F29A /* KnockedRoomProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8F4D7D61B80EBD5CB92F8A /* KnockedRoomProxyMock.swift */; };
66832DE7B5C2E861045265DC /* RoomSelectionScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D879DC5515B1D42577F96C94 /* RoomSelectionScreen.swift */; };
66E9202BED03B5BB00E812A1 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 227AC5D71A4CE43512062243 /* URL.swift */; };
67160204A8D362BB7D4AD259 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693E16574C6F7F9FA1015A8C /* Search.swift */; }; 67160204A8D362BB7D4AD259 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693E16574C6F7F9FA1015A8C /* Search.swift */; };
6786C4B0936AC84D993B20BF /* NotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */; }; 6786C4B0936AC84D993B20BF /* NotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.swift */; };
6793E75E3EBE48EBB8F857AF /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; }; 6793E75E3EBE48EBB8F857AF /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; };
@@ -561,6 +580,7 @@
7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */; }; 7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */; };
7A71AEF419904209BB8C2833 /* UserAgentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2529D434C750ED78ADF1ED /* UserAgentBuilder.swift */; }; 7A71AEF419904209BB8C2833 /* UserAgentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2529D434C750ED78ADF1ED /* UserAgentBuilder.swift */; };
7A8B264506D3DDABC01B4EEB /* AppMediator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53AC78E49A297AC1D72A7CF /* AppMediator.swift */; }; 7A8B264506D3DDABC01B4EEB /* AppMediator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53AC78E49A297AC1D72A7CF /* AppMediator.swift */; };
7AED78DC086695E93F0647D2 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
7B1605C6FFD4D195F264A684 /* RoomPollsHistoryScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */; }; 7B1605C6FFD4D195F264A684 /* RoomPollsHistoryScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */; };
7B3A59786DB2F741A1743ED0 /* PinnedEventsTimelineScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510E89B989477E5EE8E503C0 /* PinnedEventsTimelineScreenViewModelProtocol.swift */; }; 7B3A59786DB2F741A1743ED0 /* PinnedEventsTimelineScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510E89B989477E5EE8E503C0 /* PinnedEventsTimelineScreenViewModelProtocol.swift */; };
7B5DAB915357BE596529BF25 /* MapTilerStaticMapProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */; }; 7B5DAB915357BE596529BF25 /* MapTilerStaticMapProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */; };
@@ -630,6 +650,7 @@
88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */; }; 88CBF1595E39CE697928DE48 /* SFNumberedListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */; };
88F348E2CB14FF71CBBB665D /* AudioRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7475C5AE20BA896930907EA8 /* AudioRoomTimelineItemContent.swift */; }; 88F348E2CB14FF71CBBB665D /* AudioRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7475C5AE20BA896930907EA8 /* AudioRoomTimelineItemContent.swift */; };
890F0D453FE388756479AC97 /* AnalyticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C687844F60BFF532D49A994C /* AnalyticsTests.swift */; }; 890F0D453FE388756479AC97 /* AnalyticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C687844F60BFF532D49A994C /* AnalyticsTests.swift */; };
89198AE2649DD77673D5793B /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
8944548A684F1C837CEC47F4 /* RoomMembersListScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0946F77B696176E062D037 /* RoomMembersListScreenModels.swift */; }; 8944548A684F1C837CEC47F4 /* RoomMembersListScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D0946F77B696176E062D037 /* RoomMembersListScreenModels.swift */; };
89658A44C9FC19B58FD1C226 /* ServerConfirmationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */; }; 89658A44C9FC19B58FD1C226 /* ServerConfirmationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */; };
899359A4D1147601F6C4E364 /* PillConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB8D34E94AB07128DB73D6C7 /* PillConstants.swift */; }; 899359A4D1147601F6C4E364 /* PillConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB8D34E94AB07128DB73D6C7 /* PillConstants.swift */; };
@@ -656,6 +677,7 @@
8D3E1FADD78E72504DE0E402 /* UserAgentBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */; }; 8D3E1FADD78E72504DE0E402 /* UserAgentBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */; };
8D71E5E53F372202379BECCE /* BugReportScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303FCADE77DF1F3670C086ED /* BugReportScreenViewModel.swift */; }; 8D71E5E53F372202379BECCE /* BugReportScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303FCADE77DF1F3670C086ED /* BugReportScreenViewModel.swift */; };
8DC176CC5ABA24138EB443DD /* RoomMemberDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55679AF67545EF8087E47BE /* RoomMemberDetails.swift */; }; 8DC176CC5ABA24138EB443DD /* RoomMemberDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55679AF67545EF8087E47BE /* RoomMemberDetails.swift */; };
8DCA1F05C3BA6ED826F1599D /* RoomSelectionScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B4E3F1265581683E4997B8 /* RoomSelectionScreenViewModel.swift */; };
8DCD9CC5361FF22A5B2C20F1 /* AppLockSetupSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9FCE4D1E3A81AC1CC5CB91 /* AppLockSetupSettingsScreenCoordinator.swift */; }; 8DCD9CC5361FF22A5B2C20F1 /* AppLockSetupSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D9FCE4D1E3A81AC1CC5CB91 /* AppLockSetupSettingsScreenCoordinator.swift */; };
8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */; }; 8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */; };
8E650379587C31D7912ED67B /* UNNotification+Creator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0AEA686E425F86F6BA0404 /* UNNotification+Creator.swift */; }; 8E650379587C31D7912ED67B /* UNNotification+Creator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC0AEA686E425F86F6BA0404 /* UNNotification+Creator.swift */; };
@@ -694,7 +716,6 @@
9603EEF6DE980BB1D15D4707 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A3E8741D199CD1A37F4CBF /* UIView.swift */; }; 9603EEF6DE980BB1D15D4707 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05A3E8741D199CD1A37F4CBF /* UIView.swift */; };
962A4F8AD6312804E2C6BB6E /* PhotoLibraryPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A232D9156D225BD9FD1D0C43 /* PhotoLibraryPicker.swift */; }; 962A4F8AD6312804E2C6BB6E /* PhotoLibraryPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A232D9156D225BD9FD1D0C43 /* PhotoLibraryPicker.swift */; };
964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B902EA6CD3296B0E10EE432B /* HomeScreen.swift */; }; 964B9D2EC38C488C360CE0C9 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B902EA6CD3296B0E10EE432B /* HomeScreen.swift */; };
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
9696ECAFB4F0C079C5C2A526 /* AppLockSetupPINScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */; }; 9696ECAFB4F0C079C5C2A526 /* AppLockSetupPINScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */; };
96B3606E30F824095B1DD022 /* NetworkMonitorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */; }; 96B3606E30F824095B1DD022 /* NetworkMonitorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */; };
97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; }; 97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; };
@@ -848,6 +869,7 @@
B818580464CFB5400A3EF6AE /* TimelineModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029D5701F80A9AF7167BB4D0 /* TimelineModels.swift */; }; B818580464CFB5400A3EF6AE /* TimelineModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029D5701F80A9AF7167BB4D0 /* TimelineModels.swift */; };
B879446FD8E65A711EF8F9F7 /* AdvancedSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */; }; B879446FD8E65A711EF8F9F7 /* AdvancedSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */; };
B89990DD875B0B603D4D4332 /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; }; B89990DD875B0B603D4D4332 /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; };
B8EC8A544162B0A41B9AB339 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC3F82523D6F48B926D6AF68 /* AppSettings.swift */; };
B93D7CE520088AD53FA6D53C /* SettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B663BE498BB39EADC24025D /* SettingsScreenModels.swift */; }; B93D7CE520088AD53FA6D53C /* SettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B663BE498BB39EADC24025D /* SettingsScreenModels.swift */; };
B93FA0DA1504B301CAEE141B /* NotificationSettingsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F5D66F158A6662F953733E /* NotificationSettingsProxy.swift */; }; B93FA0DA1504B301CAEE141B /* NotificationSettingsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F5D66F158A6662F953733E /* NotificationSettingsProxy.swift */; };
B94368839BDB69172E28E245 /* MXLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111B698739E3410E2CDB7144 /* MXLog.swift */; }; B94368839BDB69172E28E245 /* MXLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 111B698739E3410E2CDB7144 /* MXLog.swift */; };
@@ -870,9 +892,11 @@
BDA68E8D95B2B24B28825B8B /* LoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */; }; BDA68E8D95B2B24B28825B8B /* LoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */; };
BDC4EB54CC3036730475CB8B /* QRCodeLoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25E7E9B7FEAB6169D960C206 /* QRCodeLoginScreenViewModelTests.swift */; }; BDC4EB54CC3036730475CB8B /* QRCodeLoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25E7E9B7FEAB6169D960C206 /* QRCodeLoginScreenViewModelTests.swift */; };
BDED6DA7AD1E76018C424143 /* LegalInformationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C34667458773B02AB5FB0B2 /* LegalInformationScreenViewModel.swift */; }; BDED6DA7AD1E76018C424143 /* LegalInformationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C34667458773B02AB5FB0B2 /* LegalInformationScreenViewModel.swift */; };
BE8E5985771DF9137C6CE89A /* ProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077B01C13BBA2996272C5FB5 /* ProcessInfo.swift */; };
BEA646DF302711A753F0D420 /* MapTilerStyleBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 225EFCA26877E75CDFE7F48D /* MapTilerStyleBuilderProtocol.swift */; }; BEA646DF302711A753F0D420 /* MapTilerStyleBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 225EFCA26877E75CDFE7F48D /* MapTilerStyleBuilderProtocol.swift */; };
BFEB24336DFD5F196E6F3456 /* IntentionalMentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF5CBAF69BDF5DF31C661E1 /* IntentionalMentions.swift */; }; BFEB24336DFD5F196E6F3456 /* IntentionalMentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF5CBAF69BDF5DF31C661E1 /* IntentionalMentions.swift */; };
C0090506A52A1991BAF4BA68 /* NotificationSettingsChatType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07579F9C29001E40715F3014 /* NotificationSettingsChatType.swift */; }; C0090506A52A1991BAF4BA68 /* NotificationSettingsChatType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07579F9C29001E40715F3014 /* NotificationSettingsChatType.swift */; };
C022284E2774A5E1EF683B4D /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */; };
C051475DFF4C8EBDDF4DC8E4 /* StartChatScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99E13633862847D8B7E2815 /* StartChatScreenModels.swift */; }; C051475DFF4C8EBDDF4DC8E4 /* StartChatScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = B99E13633862847D8B7E2815 /* StartChatScreenModels.swift */; };
C08AAE7563E0722C9383F51C /* RoomMembersListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B8E176484A89BAC389D4076 /* RoomMembersListScreen.swift */; }; C08AAE7563E0722C9383F51C /* RoomMembersListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B8E176484A89BAC389D4076 /* RoomMembersListScreen.swift */; };
C0B97FFEC0083F3A36609E61 /* TimelineItemMacContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = A243A6E6207297123E60DE48 /* TimelineItemMacContextMenu.swift */; }; C0B97FFEC0083F3A36609E61 /* TimelineItemMacContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = A243A6E6207297123E60DE48 /* TimelineItemMacContextMenu.swift */; };
@@ -983,11 +1007,12 @@
D6DE764B17FB4A9A12C33BF4 /* MessageComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */; }; D6DE764B17FB4A9A12C33BF4 /* MessageComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1DF3FFFE5ED2B8133F43A7 /* MessageComposer.swift */; };
D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; }; D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; };
D8459AAD6969B1431ECBE990 /* UnsupportedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E535B3388755B65C34CD10 /* UnsupportedRoomTimelineView.swift */; }; D8459AAD6969B1431ECBE990 /* UnsupportedRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9E535B3388755B65C34CD10 /* UnsupportedRoomTimelineView.swift */; };
D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
D8CFA0EE46376F9FF04EEE45 /* TextRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4853C923A1AF43711D025EAF /* TextRoomTimelineView.swift */; }; D8CFA0EE46376F9FF04EEE45 /* TextRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4853C923A1AF43711D025EAF /* TextRoomTimelineView.swift */; };
D8F1462EA00AFC939FF9ACCA /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 203D1ACC20287F8986C959D3 /* target.yml */; };
D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; }; D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; };
D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.swift */; }; D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.swift */; };
DA7E867F5EAFF8E20B2EE3B6 /* SecureBackupScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3D16709ADD4F4BCC710B1E /* SecureBackupScreenModels.swift */; }; DA7E867F5EAFF8E20B2EE3B6 /* SecureBackupScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B3D16709ADD4F4BCC710B1E /* SecureBackupScreenModels.swift */; };
DAF63A9CF9932CA8F6830F11 /* ShareExtensionModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */; };
DB079D1929B5A5F52D207C83 /* RoomDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 466C71A0FED9BFF287613C82 /* RoomDetailsScreenModels.swift */; }; DB079D1929B5A5F52D207C83 /* RoomDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 466C71A0FED9BFF287613C82 /* RoomDetailsScreenModels.swift */; };
DB65401349C143DFF883E2B0 /* AnalyticsPromptScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8EC6EA7EDFCE46710DA306 /* AnalyticsPromptScreenViewModel.swift */; }; DB65401349C143DFF883E2B0 /* AnalyticsPromptScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8EC6EA7EDFCE46710DA306 /* AnalyticsPromptScreenViewModel.swift */; };
DBC8D1DBFE9F9CA7662BC8AA /* RoomPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974AEAF3FE0C577A6C04AD6E /* RoomPermissions.swift */; }; DBC8D1DBFE9F9CA7662BC8AA /* RoomPermissions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 974AEAF3FE0C577A6C04AD6E /* RoomPermissions.swift */; };
@@ -1097,8 +1122,10 @@
F12F6BED7B6D7EE4BEE55039 /* PlainMentionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */; }; F12F6BED7B6D7EE4BEE55039 /* PlainMentionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */; };
F18CA61A58C77C84F551B8E7 /* GeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57916A1578D8043BB0795441 /* GeneratedMocks.swift */; }; F18CA61A58C77C84F551B8E7 /* GeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57916A1578D8043BB0795441 /* GeneratedMocks.swift */; };
F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; }; F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
F255083E18CDBFDF7E640FB1 /* Avatars.swift in Sources */ = {isa = PBXBuildFile; fileRef = C142248014E08E885E323E56 /* Avatars.swift */; };
F2D5C0E1351DA7BD16867629 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4823EAB4B4E8BAB4F6B8C /* TimelineStyle.swift */; }; F2D5C0E1351DA7BD16867629 /* TimelineStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD4823EAB4B4E8BAB4F6B8C /* TimelineStyle.swift */; };
F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; }; F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB7BAD55A4E2B8E5828CD64C /* SoftLogoutScreenViewModel.swift */; };
F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41A8571A8A071FB41778C016 /* ExtensionLogger.swift */; };
F3E2D3F7ACDED65A4E5CD8DE /* RoomMembersListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */; }; F3E2D3F7ACDED65A4E5CD8DE /* RoomMembersListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECF79FB25E2D4BD6F50CE7C9 /* RoomMembersListScreenViewModel.swift */; };
F3ECA377FF77E81A4F1FA062 /* TimelineItemSendInfoLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753B4C6C0EDDCBF0708DC384 /* TimelineItemSendInfoLabel.swift */; }; F3ECA377FF77E81A4F1FA062 /* TimelineItemSendInfoLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753B4C6C0EDDCBF0708DC384 /* TimelineItemSendInfoLabel.swift */; };
F3F38062C6CA21CF403C5C90 /* AudioConverterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757B1BE23DF8AA239937243 /* AudioConverterProtocol.swift */; }; F3F38062C6CA21CF403C5C90 /* AudioConverterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757B1BE23DF8AA239937243 /* AudioConverterProtocol.swift */; };
@@ -1112,6 +1139,7 @@
F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */; }; F5D2270B5021D521C0D22E11 /* FlowCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */; };
F656F92A63D3DC1978D79427 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 290FDEDA4D764B9F7EBE55A9 /* Algorithms */; }; F656F92A63D3DC1978D79427 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 290FDEDA4D764B9F7EBE55A9 /* Algorithms */; };
F669B55BC237CDA5EC9332FE /* MentionSuggestionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4100DDE6BF3C566AB66B80CC /* MentionSuggestionItemView.swift */; }; F669B55BC237CDA5EC9332FE /* MentionSuggestionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4100DDE6BF3C566AB66B80CC /* MentionSuggestionItemView.swift */; };
F66BBBE51B258BBB0B918C68 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = C79D91A7F9F378CECEF64B5A /* MatrixRustSDK */; };
F66BCCC825D6CA51724A94D0 /* MediaPlayerProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A1F98AE670377B20679FF5 /* MediaPlayerProvider.swift */; }; F66BCCC825D6CA51724A94D0 /* MediaPlayerProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8A1F98AE670377B20679FF5 /* MediaPlayerProvider.swift */; };
F697284B9B5F2C00CFEA3B12 /* EmojiDetectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E93D91DE3288010390DEE /* EmojiDetectionTests.swift */; }; F697284B9B5F2C00CFEA3B12 /* EmojiDetectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E93D91DE3288010390DEE /* EmojiDetectionTests.swift */; };
F6DFA23885980118AD7359C5 /* NotificationSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2389732B0E115A999A069083 /* NotificationSettingsScreenCoordinator.swift */; }; F6DFA23885980118AD7359C5 /* NotificationSettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2389732B0E115A999A069083 /* NotificationSettingsScreenCoordinator.swift */; };
@@ -1149,6 +1177,7 @@
FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; }; FD4C21F8DA1E273DE94FCD1A /* NotificationItemProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */; };
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; }; FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */; };
FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */; }; FDD5B4B616D9FF4DE3E9A418 /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */; };
FDE47D4686BA0F86BB584633 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = CAA3B9DF998B397C9EE64E8B /* Collections */; };
FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; }; FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; };
FEC03105D1BDE0F49BD7F243 /* PinnedEventsTimelineScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B6572E6EF5D5F4B0C338A40 /* PinnedEventsTimelineScreenModels.swift */; }; FEC03105D1BDE0F49BD7F243 /* PinnedEventsTimelineScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B6572E6EF5D5F4B0C338A40 /* PinnedEventsTimelineScreenModels.swift */; };
FEFD5290B31FCBA6999912C8 /* RoomChangePermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */; }; FEFD5290B31FCBA6999912C8 /* RoomChangePermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */; };
@@ -1173,6 +1202,13 @@
remoteGlobalIDString = C0FAEB81CFD9776CD78CE489; remoteGlobalIDString = C0FAEB81CFD9776CD78CE489;
remoteInfo = ElementX; remoteInfo = ElementX;
}; };
52A426E590105174D83B9532 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = AC22997D58D612146053154D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 19F0C845D67E9BEA4BE7133E;
remoteInfo = ShareExtension;
};
6848AF4480814C5F810FB7EB /* PBXContainerItemProxy */ = { 6848AF4480814C5F810FB7EB /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = AC22997D58D612146053154D /* Project object */; containerPortal = AC22997D58D612146053154D /* Project object */;
@@ -1204,6 +1240,7 @@
dstSubfolderSpec = 13; dstSubfolderSpec = 13;
files = ( files = (
EB88DBD77221E2CFE463018C /* NSE.appex in Embed Foundation Extensions */, EB88DBD77221E2CFE463018C /* NSE.appex in Embed Foundation Extensions */,
00C3023B6DF55024D8876B76 /* ShareExtension.appex in Embed Foundation Extensions */,
); );
name = "Embed Foundation Extensions"; name = "Embed Foundation Extensions";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -1334,6 +1371,7 @@
1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenModels.swift; sourceTree = "<group>"; }; 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenModels.swift; sourceTree = "<group>"; };
1B8E176484A89BAC389D4076 /* RoomMembersListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreen.swift; sourceTree = "<group>"; }; 1B8E176484A89BAC389D4076 /* RoomMembersListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreen.swift; sourceTree = "<group>"; };
1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxyProtocol.swift; sourceTree = "<group>"; }; 1B927CF5EF7FCCDA5EDC474B /* NotificationItemProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxyProtocol.swift; sourceTree = "<group>"; };
1B9D191A81FFB0C72CE73E77 /* RoomSelectionScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreenModels.swift; sourceTree = "<group>"; };
1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = "<group>"; }; 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LAContextMock.swift; sourceTree = "<group>"; };
1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = "<group>"; }; 1C21A715237F2B6D6E80998C /* SecureBackupControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupControllerProtocol.swift; sourceTree = "<group>"; };
1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; }; 1C25B6EBEB414431187D73B7 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
@@ -1348,6 +1386,7 @@
1D9F148717D74F73BE724434 /* LongPressWithFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressWithFeedback.swift; sourceTree = "<group>"; }; 1D9F148717D74F73BE724434 /* LongPressWithFeedback.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LongPressWithFeedback.swift; sourceTree = "<group>"; };
1DA7E93C2E148B96EF6A8500 /* TimelineItemAccessibilityModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemAccessibilityModifier.swift; sourceTree = "<group>"; }; 1DA7E93C2E148B96EF6A8500 /* TimelineItemAccessibilityModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemAccessibilityModifier.swift; sourceTree = "<group>"; };
1DB2FC2AA9A07EE792DF65CF /* NotificationPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenModels.swift; sourceTree = "<group>"; }; 1DB2FC2AA9A07EE792DF65CF /* NotificationPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenModels.swift; sourceTree = "<group>"; };
1DD2A058F3566FEEBA1D11B3 /* RoomSelectionScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreenViewModelProtocol.swift; sourceTree = "<group>"; };
1DE7969EBCAF078813E18EA1 /* RoomRolesAndPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenModels.swift; sourceTree = "<group>"; }; 1DE7969EBCAF078813E18EA1 /* RoomRolesAndPermissionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsScreenModels.swift; sourceTree = "<group>"; };
1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 1DF8F7A3AD83D04C08D75E01 /* RoomDetailsEditScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenViewModelProtocol.swift; sourceTree = "<group>"; };
1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProposedViewSize.swift; sourceTree = "<group>"; }; 1DFE0E493FB55E5A62E7852A /* ProposedViewSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProposedViewSize.swift; sourceTree = "<group>"; };
@@ -1358,6 +1397,7 @@
1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenCoordinator.swift; sourceTree = "<group>"; }; 1FAF8C2226A57B9AB7446B31 /* AppLockSetupPINScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreenCoordinator.swift; sourceTree = "<group>"; };
1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItemContent.swift; sourceTree = "<group>"; }; 1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItemContent.swift; sourceTree = "<group>"; };
201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = "<group>"; }; 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = "<group>"; };
203D1ACC20287F8986C959D3 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMapProtocol.swift; sourceTree = "<group>"; }; 20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMapProtocol.swift; sourceTree = "<group>"; };
20E69F67D2A70ABD08CA6D54 /* NotificationPermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 20E69F67D2A70ABD08CA6D54 /* NotificationPermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPermissionsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; }; 2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
@@ -1484,15 +1524,18 @@
3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = "<group>"; }; 3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = "<group>"; };
3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSettingsUITests.swift; sourceTree = "<group>"; }; 3BF8E5D4C95974B96A18C80E /* EncryptionSettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionSettingsUITests.swift; sourceTree = "<group>"; };
3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = "<group>"; }; 3C368CAB3063EF275357ECD4 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = "<group>"; };
3C3ADF21BE301D0DA48F2A7E /* ShareExtensionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionView.swift; sourceTree = "<group>"; };
3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenModels.swift; sourceTree = "<group>"; }; 3C3E67E09FE5A35D73818C39 /* AppLockScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenModels.swift; sourceTree = "<group>"; };
3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = "<group>"; }; 3CCD41CD67DB5DA0D436BFE9 /* VoiceMessageRoomPlaybackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRoomPlaybackView.swift; sourceTree = "<group>"; };
3CCE3636E3D01477C8B2E9D0 /* ReportContentScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenModels.swift; sourceTree = "<group>"; }; 3CCE3636E3D01477C8B2E9D0 /* ReportContentScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportContentScreenModels.swift; sourceTree = "<group>"; };
3CFD5EB0B0EEA4549FB49784 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; }; 3CFD5EB0B0EEA4549FB49784 /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
3D1D4A6D451F43A03CACD01D /* PINTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PINTextField.swift; sourceTree = "<group>"; }; 3D1D4A6D451F43A03CACD01D /* PINTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PINTextField.swift; sourceTree = "<group>"; };
3D27299A36536DBF91AE8FA6 /* ShareExtensionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionViewController.swift; sourceTree = "<group>"; };
3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelTests.swift; sourceTree = "<group>"; }; 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelTests.swift; sourceTree = "<group>"; };
3D4DD336905C72F95EAF34B7 /* ElementX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElementX-Bridging-Header.h"; sourceTree = "<group>"; }; 3D4DD336905C72F95EAF34B7 /* ElementX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElementX-Bridging-Header.h"; sourceTree = "<group>"; };
3D65BCC659FD9087E49B3C25 /* AppAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAppearance.swift; sourceTree = "<group>"; }; 3D65BCC659FD9087E49B3C25 /* AppAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAppearance.swift; sourceTree = "<group>"; };
3D75941CBD7D336F831924EC /* ReadReceiptCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadReceiptCell.swift; sourceTree = "<group>"; }; 3D75941CBD7D336F831924EC /* ReadReceiptCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadReceiptCell.swift; sourceTree = "<group>"; };
3D8BEEFCA07BEA43F4F4BF77 /* ShareExtension.appex */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = "wrapper.app-extension"; path = ShareExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
3D9B45D584D232CB9E5C7734 /* RoomChangeRolesScreenSelectedItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenSelectedItem.swift; sourceTree = "<group>"; }; 3D9B45D584D232CB9E5C7734 /* RoomChangeRolesScreenSelectedItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenSelectedItem.swift; sourceTree = "<group>"; };
3D9FCE4D1E3A81AC1CC5CB91 /* AppLockSetupSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenCoordinator.swift; sourceTree = "<group>"; }; 3D9FCE4D1E3A81AC1CC5CB91 /* AppLockSetupSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenCoordinator.swift; sourceTree = "<group>"; };
3DBE70FFB7936F35811772C1 /* IdentityConfirmedScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenModels.swift; sourceTree = "<group>"; }; 3DBE70FFB7936F35811772C1 /* IdentityConfirmedScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenModels.swift; sourceTree = "<group>"; };
@@ -1512,6 +1555,7 @@
4137900E28201C314C835C11 /* RoomScreenFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenFooterView.swift; sourceTree = "<group>"; }; 4137900E28201C314C835C11 /* RoomScreenFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenFooterView.swift; sourceTree = "<group>"; };
4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreen.swift; sourceTree = "<group>"; }; 4176C3E20C772DE8D182863C /* LegalInformationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreen.swift; sourceTree = "<group>"; };
419957D7B1C983D7B3B93678 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; 419957D7B1C983D7B3B93678 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
41A8571A8A071FB41778C016 /* ExtensionLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionLogger.swift; sourceTree = "<group>"; };
41BB37D96C3EA18F3CE8675D /* RoomDirectorySearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenModels.swift; sourceTree = "<group>"; }; 41BB37D96C3EA18F3CE8675D /* RoomDirectorySearchScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenModels.swift; sourceTree = "<group>"; };
41D041A857614A9AE13C7795 /* RoomChangePermissionsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelTests.swift; sourceTree = "<group>"; }; 41D041A857614A9AE13C7795 /* RoomChangePermissionsScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelTests.swift; sourceTree = "<group>"; };
421E716C521F96D24ECE69B3 /* NoticeRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItem.swift; sourceTree = "<group>"; }; 421E716C521F96D24ECE69B3 /* NoticeRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItem.swift; sourceTree = "<group>"; };
@@ -1581,6 +1625,7 @@
4FDD775CFD72DD2D3C8A8390 /* NotificationSettingsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsProxyProtocol.swift; sourceTree = "<group>"; }; 4FDD775CFD72DD2D3C8A8390 /* NotificationSettingsProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsProxyProtocol.swift; sourceTree = "<group>"; };
502F986D57158674172C58E3 /* AppLockSetupSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenModels.swift; sourceTree = "<group>"; }; 502F986D57158674172C58E3 /* AppLockSetupSettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenModels.swift; sourceTree = "<group>"; };
505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; }; 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
505ADA084C0B38A0C4AD2574 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItem.swift; sourceTree = "<group>"; }; 5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItem.swift; sourceTree = "<group>"; };
50D685B4DB38BB5BD87C956A /* AuthenticationStartScreenBackgroundImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenBackgroundImage.swift; sourceTree = "<group>"; }; 50D685B4DB38BB5BD87C956A /* AuthenticationStartScreenBackgroundImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationStartScreenBackgroundImage.swift; sourceTree = "<group>"; };
50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixUserShareLink.swift; sourceTree = "<group>"; }; 50E31AB0E77BB70E2BC77463 /* MatrixUserShareLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixUserShareLink.swift; sourceTree = "<group>"; };
@@ -1788,6 +1833,7 @@
8319173DD66C07F45DC48848 /* IdentityConfirmedScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 8319173DD66C07F45DC48848 /* IdentityConfirmedScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenViewModelProtocol.swift; sourceTree = "<group>"; };
837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModel.swift; sourceTree = "<group>"; }; 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModel.swift; sourceTree = "<group>"; };
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCornerShape.swift; sourceTree = "<group>"; }; 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCornerShape.swift; sourceTree = "<group>"; };
83B4E3F1265581683E4997B8 /* RoomSelectionScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreenViewModel.swift; sourceTree = "<group>"; };
84311D707B09854D67F78BBF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 84311D707B09854D67F78BBF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = "<group>"; }; 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = "<group>"; };
848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; }; 848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; };
@@ -2034,6 +2080,7 @@
B81B6170DB690013CEB646F4 /* MapLibreModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreModels.swift; sourceTree = "<group>"; }; B81B6170DB690013CEB646F4 /* MapLibreModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreModels.swift; sourceTree = "<group>"; };
B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageButton.swift; sourceTree = "<group>"; }; B8516302ACCA94A0E680AB3B /* VoiceMessageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageButton.swift; sourceTree = "<group>"; };
B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregratedReaction.swift; sourceTree = "<group>"; }; B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AggregratedReaction.swift; sourceTree = "<group>"; };
B88CE0A058727BC68EEEC6B6 /* ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShareExtension.entitlements; sourceTree = "<group>"; };
B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPicker.swift; sourceTree = "<group>"; }; B8A3B7637DDBD6AA97AC2545 /* CameraPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraPicker.swift; sourceTree = "<group>"; };
B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCoordinators.swift; sourceTree = "<group>"; }; B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCoordinators.swift; sourceTree = "<group>"; };
B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; }; B902EA6CD3296B0E10EE432B /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
@@ -2068,6 +2115,7 @@
C0900BBF0A5D5D775E917C70 /* EventBasedMessageTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedMessageTimelineItemProtocol.swift; sourceTree = "<group>"; }; C0900BBF0A5D5D775E917C70 /* EventBasedMessageTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedMessageTimelineItemProtocol.swift; sourceTree = "<group>"; };
C0FEA560929DD73FFEF8C3DF /* HomeScreenEmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenEmptyStateView.swift; sourceTree = "<group>"; }; C0FEA560929DD73FFEF8C3DF /* HomeScreenEmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenEmptyStateView.swift; sourceTree = "<group>"; };
C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupRecoveryKeyScreenViewModelTests.swift; sourceTree = "<group>"; }; C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupRecoveryKeyScreenViewModelTests.swift; sourceTree = "<group>"; };
C142248014E08E885E323E56 /* Avatars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatars.swift; sourceTree = "<group>"; };
C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutDirection.swift; sourceTree = "<group>"; }; C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutDirection.swift; sourceTree = "<group>"; };
C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationModeProxy.swift; sourceTree = "<group>"; }; C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationModeProxy.swift; sourceTree = "<group>"; };
C15E0017717EAE3A1D02D005 /* StaticLocationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenCoordinator.swift; sourceTree = "<group>"; }; C15E0017717EAE3A1D02D005 /* StaticLocationScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StaticLocationScreenCoordinator.swift; sourceTree = "<group>"; };
@@ -2150,7 +2198,6 @@
D33116993D54FADC0C721C1F /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; }; D33116993D54FADC0C721C1F /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
D38391154120264910D19528 /* PollMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollMock.swift; sourceTree = "<group>"; }; D38391154120264910D19528 /* PollMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollMock.swift; sourceTree = "<group>"; };
D39D7F513A36C9C1951DB44C /* AnalyticsSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreen.swift; sourceTree = "<group>"; }; D39D7F513A36C9C1951DB44C /* AnalyticsSettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettingsScreen.swift; sourceTree = "<group>"; };
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = "<group>"; };
D3F219838588C62198E726E3 /* LABiometryType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LABiometryType.swift; sourceTree = "<group>"; }; D3F219838588C62198E726E3 /* LABiometryType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LABiometryType.swift; sourceTree = "<group>"; };
D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreen.swift; sourceTree = "<group>"; }; D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupPINScreen.swift; sourceTree = "<group>"; };
D45C9EAA86423D7D3126DE4F /* VoiceMessageRecorderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorderProtocol.swift; sourceTree = "<group>"; }; D45C9EAA86423D7D3126DE4F /* VoiceMessageRecorderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorderProtocol.swift; sourceTree = "<group>"; };
@@ -2173,6 +2220,7 @@
D79BB714D28C9F588DD69353 /* SecureBackupScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenViewModelProtocol.swift; sourceTree = "<group>"; }; D79BB714D28C9F588DD69353 /* SecureBackupScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenViewModelProtocol.swift; sourceTree = "<group>"; };
D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentBuilder.swift; sourceTree = "<group>"; }; D7BB243B26D54EF1A0C422C0 /* NotificationContentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationContentBuilder.swift; sourceTree = "<group>"; };
D7BEB970F500BFB248443FA1 /* BloomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloomView.swift; sourceTree = "<group>"; }; D7BEB970F500BFB248443FA1 /* BloomView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloomView.swift; sourceTree = "<group>"; };
D879DC5515B1D42577F96C94 /* RoomSelectionScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreen.swift; sourceTree = "<group>"; };
D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModel.swift; sourceTree = "<group>"; }; D8E60332509665C00179ACF6 /* MessageForwardingScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModel.swift; sourceTree = "<group>"; };
D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineItem.swift; sourceTree = "<group>"; }; D8F5F9E02B1AB5350B1815E7 /* TimelineStartRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineItem.swift; sourceTree = "<group>"; };
D8FC33C3F6BF597E095CE9FA /* HomeScreenInviteCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenInviteCell.swift; sourceTree = "<group>"; }; D8FC33C3F6BF597E095CE9FA /* HomeScreenInviteCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenInviteCell.swift; sourceTree = "<group>"; };
@@ -2187,6 +2235,7 @@
DC0AEA686E425F86F6BA0404 /* UNNotification+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNNotification+Creator.swift"; sourceTree = "<group>"; }; DC0AEA686E425F86F6BA0404 /* UNNotification+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNNotification+Creator.swift"; sourceTree = "<group>"; };
DC10CCC8D68B863E20660DBC /* MessageForwardingScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModelProtocol.swift; sourceTree = "<group>"; }; DC10CCC8D68B863E20660DBC /* MessageForwardingScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageForwardingScreenViewModelProtocol.swift; sourceTree = "<group>"; };
DC528B3764E3CF7FCFEF40E7 /* PollInteractionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollInteractionHandler.swift; sourceTree = "<group>"; }; DC528B3764E3CF7FCFEF40E7 /* PollInteractionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollInteractionHandler.swift; sourceTree = "<group>"; };
DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareExtensionModels.swift; sourceTree = "<group>"; };
DCF239C619971FDE48132550 /* SecureBackupLogoutConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenModels.swift; sourceTree = "<group>"; }; DCF239C619971FDE48132550 /* SecureBackupLogoutConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenModels.swift; sourceTree = "<group>"; };
DD97F9661ABF08CE002054A2 /* AppLockServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceTests.swift; sourceTree = "<group>"; }; DD97F9661ABF08CE002054A2 /* AppLockServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceTests.swift; sourceTree = "<group>"; };
DE5127D6EA05B2E45D0A7D59 /* JoinRoomScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModelTests.swift; sourceTree = "<group>"; }; DE5127D6EA05B2E45D0A7D59 /* JoinRoomScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenViewModelTests.swift; sourceTree = "<group>"; };
@@ -2205,7 +2254,6 @@
E1A5FEF17ED7E6176D922D4F /* RoomDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreen.swift; sourceTree = "<group>"; }; E1A5FEF17ED7E6176D922D4F /* RoomDetailsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreen.swift; sourceTree = "<group>"; };
E1E0B4A34E69BD2132BEC521 /* MessageText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageText.swift; sourceTree = "<group>"; }; E1E0B4A34E69BD2132BEC521 /* MessageText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageText.swift; sourceTree = "<group>"; };
E1ED17433ADC77287F8904F9 /* CallNotificationRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallNotificationRoomTimelineItem.swift; sourceTree = "<group>"; }; E1ED17433ADC77287F8904F9 /* CallNotificationRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallNotificationRoomTimelineItem.swift; sourceTree = "<group>"; };
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSize.swift; sourceTree = "<group>"; };
E26C69EC1157D71CC61ADAE4 /* ScaledPaddingModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaledPaddingModifier.swift; sourceTree = "<group>"; }; E26C69EC1157D71CC61ADAE4 /* ScaledPaddingModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaledPaddingModifier.swift; sourceTree = "<group>"; };
E2B1CC9AA154F4D5435BF60A /* Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comparable.swift; sourceTree = "<group>"; }; E2B1CC9AA154F4D5435BF60A /* Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comparable.swift; sourceTree = "<group>"; };
E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxyMock.swift; sourceTree = "<group>"; }; E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxyMock.swift; sourceTree = "<group>"; };
@@ -2284,6 +2332,7 @@
F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; }; F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; };
F0B9F5BC4C80543DE7228B9D /* MapTilerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyle.swift; sourceTree = "<group>"; }; F0B9F5BC4C80543DE7228B9D /* MapTilerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyle.swift; sourceTree = "<group>"; };
F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModel.swift; sourceTree = "<group>"; }; F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModel.swift; sourceTree = "<group>"; };
F104596B0620CEFE5DFD31B1 /* RoomSelectionScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSelectionScreenCoordinator.swift; sourceTree = "<group>"; };
F134D2D91DFF732FB75B2CB7 /* UserProfileScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileScreenViewModelProtocol.swift; sourceTree = "<group>"; }; F134D2D91DFF732FB75B2CB7 /* UserProfileScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfileScreenViewModelProtocol.swift; sourceTree = "<group>"; };
F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = "<group>"; }; F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = "<group>"; };
F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; }; F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; };
@@ -2355,6 +2404,16 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
7E8EB7CD881C54161D4474E5 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F66BBBE51B258BBB0B918C68 /* MatrixRustSDK in Frameworks */,
FDE47D4686BA0F86BB584633 /* Collections in Frameworks */,
558F37B1A8F2C4CC9B1ACEDA /* Compound in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BF59B36A7B2DB184B62826F6 /* Frameworks */ = { BF59B36A7B2DB184B62826F6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -2495,6 +2554,7 @@
06501F0E978B2D5C92771DC7 /* Logging */ = { 06501F0E978B2D5C92771DC7 /* Logging */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
41A8571A8A071FB41778C016 /* ExtensionLogger.swift */,
111B698739E3410E2CDB7144 /* MXLog.swift */, 111B698739E3410E2CDB7144 /* MXLog.swift */,
542D4F49FABA056DEEEB3400 /* RustTracing.swift */, 542D4F49FABA056DEEEB3400 /* RustTracing.swift */,
ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */, ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */,
@@ -2866,6 +2926,18 @@
path = View; path = View;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
2E42D43DB6835A58D88B2F91 /* RoomSelectionScreen */ = {
isa = PBXGroup;
children = (
F104596B0620CEFE5DFD31B1 /* RoomSelectionScreenCoordinator.swift */,
1B9D191A81FFB0C72CE73E77 /* RoomSelectionScreenModels.swift */,
83B4E3F1265581683E4997B8 /* RoomSelectionScreenViewModel.swift */,
1DD2A058F3566FEEBA1D11B3 /* RoomSelectionScreenViewModelProtocol.swift */,
FF654D7FD6693839E3185FAD /* View */,
);
path = RoomSelectionScreen;
sourceTree = "<group>";
};
2ECFF6B05DAA37EB10DBF7E8 /* UITests */ = { 2ECFF6B05DAA37EB10DBF7E8 /* UITests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -3221,10 +3293,21 @@
823ED0EC3F1B6CF47D284011 /* Tools */, 823ED0EC3F1B6CF47D284011 /* Tools */,
B04B538A859CD012755DC19C /* NSE */, B04B538A859CD012755DC19C /* NSE */,
1803CD2B96BF06009334BB61 /* PreviewTests */, 1803CD2B96BF06009334BB61 /* PreviewTests */,
D0111119CDF3E28E6D7768E8 /* ShareExtension */,
681566846AF307E9BA4C72C6 /* Products */, 681566846AF307E9BA4C72C6 /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
}; };
40D9A816C45E0278C29DF883 /* SupportingFiles */ = {
isa = PBXGroup;
children = (
505ADA084C0B38A0C4AD2574 /* Info.plist */,
B88CE0A058727BC68EEEC6B6 /* ShareExtension.entitlements */,
203D1ACC20287F8986C959D3 /* target.yml */,
);
path = SupportingFiles;
sourceTree = "<group>";
};
40E6246F03D1FE377BC5D963 /* Room */ = { 40E6246F03D1FE377BC5D963 /* Room */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -3518,11 +3601,18 @@
path = BugReportScreen; path = BugReportScreen;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
557C534BD2052BFFD810CE3D /* ShareExtension */ = {
isa = PBXGroup;
children = (
DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */,
);
path = ShareExtension;
sourceTree = "<group>";
};
566F2B84465726112B830CF6 /* Other */ = { 566F2B84465726112B830CF6 /* Other */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4959CECEC984B3995616F427 /* DataProtectionManager.swift */, 4959CECEC984B3995616F427 /* DataProtectionManager.swift */,
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */,
EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */, EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */,
49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */, 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */,
); );
@@ -3698,6 +3788,7 @@
9C7F7DE62D33C6A26CBFCD72 /* IntegrationTests.xctest */, 9C7F7DE62D33C6A26CBFCD72 /* IntegrationTests.xctest */,
0D8F620C8B314840D8602E3F /* NSE.appex */, 0D8F620C8B314840D8602E3F /* NSE.appex */,
D95E8C0EFEC0C6F96EDAA71A /* PreviewTests.xctest */, D95E8C0EFEC0C6F96EDAA71A /* PreviewTests.xctest */,
3D8BEEFCA07BEA43F4F4BF77 /* ShareExtension.appex */,
F506C6ADB1E1DA6638078E11 /* UITests.xctest */, F506C6ADB1E1DA6638078E11 /* UITests.xctest */,
AAC9344689121887B74877AF /* UnitTests.xctest */, AAC9344689121887B74877AF /* UnitTests.xctest */,
); );
@@ -4144,6 +4235,14 @@
path = Replies; path = Replies;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
7FF02C3DED8CD9890375D9FF /* View */ = {
isa = PBXGroup;
children = (
3C3ADF21BE301D0DA48F2A7E /* ShareExtensionView.swift */,
);
path = View;
sourceTree = "<group>";
};
8039515BAA53B7C3275AC64A /* Client */ = { 8039515BAA53B7C3275AC64A /* Client */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -4908,7 +5007,7 @@
children = ( children = (
04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */, 04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */,
3D65BCC659FD9087E49B3C25 /* AppAppearance.swift */, 3D65BCC659FD9087E49B3C25 /* AppAppearance.swift */,
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */, C142248014E08E885E323E56 /* Avatars.swift */,
E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */, E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */,
9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */, 9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */,
AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */, AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */,
@@ -5105,6 +5204,15 @@
path = Layout; path = Layout;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D0111119CDF3E28E6D7768E8 /* ShareExtension */ = {
isa = PBXGroup;
children = (
F08E29610C82E4201463C4A5 /* Sources */,
40D9A816C45E0278C29DF883 /* SupportingFiles */,
);
path = ShareExtension;
sourceTree = "<group>";
};
D382E465AF067C1BF888BF8E /* View */ = { D382E465AF067C1BF888BF8E /* View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -5295,6 +5403,7 @@
D57B3BC211BB74420C9138D7 /* RoomPollsHistoryScreen */, D57B3BC211BB74420C9138D7 /* RoomPollsHistoryScreen */,
7B890CCD20B037760BFDF957 /* RoomRolesAndPermissionsScreen */, 7B890CCD20B037760BFDF957 /* RoomRolesAndPermissionsScreen */,
679E9837ECA8D6776079D16E /* RoomScreen */, 679E9837ECA8D6776079D16E /* RoomScreen */,
2E42D43DB6835A58D88B2F91 /* RoomSelectionScreen */,
2565414373E6F68005966B8E /* SecureBackup */, 2565414373E6F68005966B8E /* SecureBackup */,
70B74A432C241E56A7ACE610 /* Settings */, 70B74A432C241E56A7ACE610 /* Settings */,
EC4545C7E37E8294D3FE6800 /* StartChatScreen */, EC4545C7E37E8294D3FE6800 /* StartChatScreen */,
@@ -5334,6 +5443,7 @@
22F9F1514B91803BB4B88894 /* AppHooks */, 22F9F1514B91803BB4B88894 /* AppHooks */,
337015ADFBA3AB96660DB3A6 /* Generated */, 337015ADFBA3AB96660DB3A6 /* Generated */,
31CE4DA53232AA534057F912 /* Mocks */, 31CE4DA53232AA534057F912 /* Mocks */,
557C534BD2052BFFD810CE3D /* ShareExtension */,
4C826614718790C58C17117F /* UnitTests */, 4C826614718790C58C17117F /* UnitTests */,
); );
path = Sources; path = Sources;
@@ -5426,6 +5536,15 @@
path = BlockedUsersScreen; path = BlockedUsersScreen;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
F08E29610C82E4201463C4A5 /* Sources */ = {
isa = PBXGroup;
children = (
3D27299A36536DBF91AE8FA6 /* ShareExtensionViewController.swift */,
7FF02C3DED8CD9890375D9FF /* View */,
);
path = Sources;
sourceTree = "<group>";
};
F12966DF3DA87FEF21348D60 /* InviteUsersScreen */ = { F12966DF3DA87FEF21348D60 /* InviteUsersScreen */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -5567,6 +5686,14 @@
path = View; path = View;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
FF654D7FD6693839E3185FAD /* View */ = {
isa = PBXGroup;
children = (
D879DC5515B1D42577F96C94 /* RoomSelectionScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
FFD7C58CA6A7D6BBC2F584B5 /* JoinRoomScreen */ = { FFD7C58CA6A7D6BBC2F584B5 /* JoinRoomScreen */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -5616,6 +5743,28 @@
productReference = F506C6ADB1E1DA6638078E11 /* UITests.xctest */; productReference = F506C6ADB1E1DA6638078E11 /* UITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing"; productType = "com.apple.product-type.bundle.ui-testing";
}; };
19F0C845D67E9BEA4BE7133E /* ShareExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = A60414DDC2A95B206C91D4A4 /* Build configuration list for PBXNativeTarget "ShareExtension" */;
buildPhases = (
8431C24C3AE0AA27308F4185 /* Sources */,
087F14F27D0A6FDFB80392A1 /* Resources */,
7E8EB7CD881C54161D4474E5 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = ShareExtension;
packageProductDependencies = (
C79D91A7F9F378CECEF64B5A /* MatrixRustSDK */,
CAA3B9DF998B397C9EE64E8B /* Collections */,
3262F08E1C3483C22A7A319F /* Compound */,
);
productName = ShareExtension;
productReference = 3D8BEEFCA07BEA43F4F4BF77 /* ShareExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
32C23C8D224D46EFE62AFAD0 /* UnitTests */ = { 32C23C8D224D46EFE62AFAD0 /* UnitTests */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 79663128986C62EFAC289176 /* Build configuration list for PBXNativeTarget "UnitTests" */; buildConfigurationList = 79663128986C62EFAC289176 /* Build configuration list for PBXNativeTarget "UnitTests" */;
@@ -5672,6 +5821,7 @@
); );
dependencies = ( dependencies = (
2C29670603B37E38705D5FF1 /* PBXTargetDependency */, 2C29670603B37E38705D5FF1 /* PBXTargetDependency */,
58C473A5DEA945AACFEA8E9F /* PBXTargetDependency */,
); );
name = ElementX; name = ElementX;
packageProductDependencies = ( packageProductDependencies = (
@@ -5774,6 +5924,9 @@
DevelopmentTeam = 7J4U792NQT; DevelopmentTeam = 7J4U792NQT;
TestTargetID = C0FAEB81CFD9776CD78CE489; TestTargetID = C0FAEB81CFD9776CD78CE489;
}; };
19F0C845D67E9BEA4BE7133E = {
DevelopmentTeam = "$(DEVELOPMENT_TEAM)";
};
32C23C8D224D46EFE62AFAD0 = { 32C23C8D224D46EFE62AFAD0 = {
DevelopmentTeam = 7J4U792NQT; DevelopmentTeam = 7J4U792NQT;
}; };
@@ -5862,6 +6015,7 @@
FEB53A5BC378C913769656D8 /* NSE */, FEB53A5BC378C913769656D8 /* NSE */,
F8E276FD6DC43EADB85241BC /* Periphery */, F8E276FD6DC43EADB85241BC /* Periphery */,
7A17BE29BAC81ADBAC6349D9 /* PreviewTests */, 7A17BE29BAC81ADBAC6349D9 /* PreviewTests */,
19F0C845D67E9BEA4BE7133E /* ShareExtension */,
0E28CD62691FDBC63147D5E3 /* UITests */, 0E28CD62691FDBC63147D5E3 /* UITests */,
32C23C8D224D46EFE62AFAD0 /* UnitTests */, 32C23C8D224D46EFE62AFAD0 /* UnitTests */,
); );
@@ -5869,6 +6023,14 @@
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
087F14F27D0A6FDFB80392A1 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D8F1462EA00AFC939FF9ACCA /* target.yml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
215E1D91B98672C856F559D0 /* Resources */ = { 215E1D91B98672C856F559D0 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -6071,7 +6233,7 @@
484202C5D50983442D24D061 /* AttributedString.swift in Sources */, 484202C5D50983442D24D061 /* AttributedString.swift in Sources */,
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */, CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */,
BA43D782BE85C7F5F20C624A /* AttributedStringBuilderProtocol.swift in Sources */, BA43D782BE85C7F5F20C624A /* AttributedStringBuilderProtocol.swift in Sources */,
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */, F255083E18CDBFDF7E640FB1 /* Avatars.swift in Sources */,
9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */, 9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */,
238D561CA231339C6D4D06F3 /* ClientBuilder.swift in Sources */, 238D561CA231339C6D4D06F3 /* ClientBuilder.swift in Sources */,
0BAF83521871E69D222EE8E4 /* ClientBuilderHook.swift in Sources */, 0BAF83521871E69D222EE8E4 /* ClientBuilderHook.swift in Sources */,
@@ -6081,6 +6243,7 @@
24A75F72EEB7561B82D726FD /* Date.swift in Sources */, 24A75F72EEB7561B82D726FD /* Date.swift in Sources */,
9F11B9F347F9E2D236799FB3 /* ElementCallServiceConstants.swift in Sources */, 9F11B9F347F9E2D236799FB3 /* ElementCallServiceConstants.swift in Sources */,
CFEC53440C572CEEABC4A6A0 /* ElementXAttributeScope.swift in Sources */, CFEC53440C572CEEABC4A6A0 /* ElementXAttributeScope.swift in Sources */,
89198AE2649DD77673D5793B /* ExtensionLogger.swift in Sources */,
A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */, A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */,
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */, 59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */,
EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */, EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */,
@@ -6095,7 +6258,6 @@
9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */, 9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */,
7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */, 7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */,
E2DB696117BAEABAD5718023 /* MediaSourceProxy.swift in Sources */, E2DB696117BAEABAD5718023 /* MediaSourceProxy.swift in Sources */,
5455147CAC63F71E48F7D699 /* NSELogger.swift in Sources */,
30CC4F796B27BE8B1DFDBF5A /* NSEUserSession.swift in Sources */, 30CC4F796B27BE8B1DFDBF5A /* NSEUserSession.swift in Sources */,
1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */, 1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */,
94F0B78928E952689ACDB271 /* NetworkMonitor.swift in Sources */, 94F0B78928E952689ACDB271 /* NetworkMonitor.swift in Sources */,
@@ -6267,6 +6429,30 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
8431C24C3AE0AA27308F4185 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B8EC8A544162B0A41B9AB339 /* AppSettings.swift in Sources */,
2F2906AE9BC3D0E79A6F98F8 /* Bundle.swift in Sources */,
F38D32C1B0232AAFE6A0822C /* ExtensionLogger.swift in Sources */,
C022284E2774A5E1EF683B4D /* FileManager.swift in Sources */,
05FF0CD80EDAB3A7C0D4700A /* InfoPlistReader.swift in Sources */,
0638CBDE3098B1C3F23AFCFA /* MXLog.swift in Sources */,
1A3783005E6945F8583AF997 /* NSItemProvider.swift in Sources */,
BE8E5985771DF9137C6CE89A /* ProcessInfo.swift in Sources */,
7AED78DC086695E93F0647D2 /* RustTracing.swift in Sources */,
DAF63A9CF9932CA8F6830F11 /* ShareExtensionModels.swift in Sources */,
5AA81A4E2D40A32A9E7F71F2 /* ShareExtensionView.swift in Sources */,
5AC5CD6D893073EE4D9A277E /* ShareExtensionViewController.swift in Sources */,
069358C2C825A19DE6CB127E /* TracingConfiguration.swift in Sources */,
03BD83E8BDD23AE059802E0D /* UITestsScreenIdentifier.swift in Sources */,
26252AA9AED64010788F4C26 /* UIView.swift in Sources */,
66E9202BED03B5BB00E812A1 /* URL.swift in Sources */,
4E22086585CB3B35FEEFBBB9 /* UserPreference.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9797D588420FCBBC228A63C9 /* Sources */ = { 9797D588420FCBBC228A63C9 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@@ -6371,7 +6557,7 @@
874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */, 874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */,
6146996D5C4DDD5DA816FC87 /* AuthenticationTextFieldStyle.swift in Sources */, 6146996D5C4DDD5DA816FC87 /* AuthenticationTextFieldStyle.swift in Sources */,
4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */, 4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */,
D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */, 1621BF6316FFFEF5AE067C77 /* Avatars.swift in Sources */,
7A25D6926A2C01DB8D0D67A5 /* BadgeLabel.swift in Sources */, 7A25D6926A2C01DB8D0D67A5 /* BadgeLabel.swift in Sources */,
A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */, A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */,
FC0EEFF630F34899953BB950 /* BigIcon.swift in Sources */, FC0EEFF630F34899953BB950 /* BigIcon.swift in Sources */,
@@ -6509,6 +6695,7 @@
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */, 02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */,
2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */, 2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */,
63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */, 63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */,
36206F74DDEBF9BEAF6A6A1F /* ExtensionLogger.swift in Sources */,
5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */, 5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */,
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */, D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */,
37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */, 37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */,
@@ -6860,6 +7047,11 @@
352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */, 352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */,
7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.swift in Sources */, 7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.swift in Sources */,
617624A97BDBB75ED3DD8156 /* RoomScreenViewModelProtocol.swift in Sources */, 617624A97BDBB75ED3DD8156 /* RoomScreenViewModelProtocol.swift in Sources */,
66832DE7B5C2E861045265DC /* RoomSelectionScreen.swift in Sources */,
18FDE4ED6D83B0771452B43D /* RoomSelectionScreenCoordinator.swift in Sources */,
27FEF0F40750465195C9D6D6 /* RoomSelectionScreenModels.swift in Sources */,
8DCA1F05C3BA6ED826F1599D /* RoomSelectionScreenViewModel.swift in Sources */,
39DFC4B9EB6A8757210BDEC6 /* RoomSelectionScreenViewModelProtocol.swift in Sources */,
6C34237AFB808E38FC8776B9 /* RoomStateEventStringBuilder.swift in Sources */, 6C34237AFB808E38FC8776B9 /* RoomStateEventStringBuilder.swift in Sources */,
59C41313AED7566C3AC51163 /* RoomSummary.swift in Sources */, 59C41313AED7566C3AC51163 /* RoomSummary.swift in Sources */,
983896D611ABF52A5C37498D /* RoomSummaryProvider.swift in Sources */, 983896D611ABF52A5C37498D /* RoomSummaryProvider.swift in Sources */,
@@ -6945,6 +7137,7 @@
B93D7CE520088AD53FA6D53C /* SettingsScreenModels.swift in Sources */, B93D7CE520088AD53FA6D53C /* SettingsScreenModels.swift in Sources */,
E0B6A569AC3E81D233B43D60 /* SettingsScreenViewModel.swift in Sources */, E0B6A569AC3E81D233B43D60 /* SettingsScreenViewModel.swift in Sources */,
A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */, A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */,
5D99F63CC88BB29383019FC6 /* ShareExtensionModels.swift in Sources */,
1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */, 1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */,
274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */, 274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */,
77920AFA8091AC6B9F190C90 /* Signposter.swift in Sources */, 77920AFA8091AC6B9F190C90 /* Signposter.swift in Sources */,
@@ -7174,6 +7367,11 @@
target = C0FAEB81CFD9776CD78CE489 /* ElementX */; target = C0FAEB81CFD9776CD78CE489 /* ElementX */;
targetProxy = 6848AF4480814C5F810FB7EB /* PBXContainerItemProxy */; targetProxy = 6848AF4480814C5F810FB7EB /* PBXContainerItemProxy */;
}; };
58C473A5DEA945AACFEA8E9F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 19F0C845D67E9BEA4BE7133E /* ShareExtension */;
targetProxy = 52A426E590105174D83B9532 /* PBXContainerItemProxy */;
};
8E24DC048A099AAFEE13B4F5 /* PBXTargetDependency */ = { 8E24DC048A099AAFEE13B4F5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = C0FAEB81CFD9776CD78CE489 /* ElementX */; target = C0FAEB81CFD9776CD78CE489 /* ElementX */;
@@ -7445,6 +7643,27 @@
}; };
name = Debug; name = Debug;
}; };
7620CDAB1B38B30431DA8878 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = ShareExtension/SupportingFiles/ShareExtension.entitlements;
CURRENT_PROJECT_VERSION = "$(CURRENT_PROJECT_VERSION)";
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
INFOPLIST_FILE = ShareExtension/SupportingFiles/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = "$(MARKETING_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.shareextension";
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
7A90A3EBE1ABAB9EAE0952F0 /* Release */ = { 7A90A3EBE1ABAB9EAE0952F0 /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@@ -7697,6 +7916,27 @@
}; };
name = Release; name = Release;
}; };
E57C898C511BBC8215673DEF /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = ShareExtension/SupportingFiles/ShareExtension.entitlements;
CURRENT_PROJECT_VERSION = "$(CURRENT_PROJECT_VERSION)";
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
INFOPLIST_FILE = ShareExtension/SupportingFiles/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = "$(MARKETING_VERSION)";
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.shareextension";
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
PRODUCT_NAME = ShareExtension;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
F0A74453D306F668178A859E /* Release */ = { F0A74453D306F668178A859E /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@@ -7754,6 +7994,15 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug; defaultConfigurationName = Debug;
}; };
A60414DDC2A95B206C91D4A4 /* Build configuration list for PBXNativeTarget "ShareExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
7620CDAB1B38B30431DA8878 /* Debug */,
E57C898C511BBC8215673DEF /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
B15427F8699AD5A5FC75C17E /* Build configuration list for PBXNativeTarget "ElementX" */ = { B15427F8699AD5A5FC75C17E /* Build configuration list for PBXNativeTarget "ElementX" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
@@ -8068,6 +8317,11 @@
package = EC6D0C817B1C21D9D096505A /* XCRemoteSwiftPackageReference "Version" */; package = EC6D0C817B1C21D9D096505A /* XCRemoteSwiftPackageReference "Version" */;
productName = Version; productName = Version;
}; };
3262F08E1C3483C22A7A319F /* Compound */ = {
isa = XCSwiftPackageProductDependency;
package = F71C70A4404CC6D9C4AF35F2 /* XCRemoteSwiftPackageReference "compound-ios" */;
productName = Compound;
};
36B7FC232711031AA2B0D188 /* DTCoreText */ = { 36B7FC232711031AA2B0D188 /* DTCoreText */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */; package = C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */;
@@ -8253,11 +8507,21 @@
package = 6FC4820D8D4559CEECA064D7 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */; package = 6FC4820D8D4559CEECA064D7 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */;
productName = MatrixRustSDK; productName = MatrixRustSDK;
}; };
C79D91A7F9F378CECEF64B5A /* MatrixRustSDK */ = {
isa = XCSwiftPackageProductDependency;
package = 6FC4820D8D4559CEECA064D7 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */;
productName = MatrixRustSDK;
};
CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */ = { CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = EE40B0E16A55BD23ECBFFD22 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */; package = EE40B0E16A55BD23ECBFFD22 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */;
productName = WysiwygComposer; productName = WysiwygComposer;
}; };
CAA3B9DF998B397C9EE64E8B /* Collections */ = {
isa = XCSwiftPackageProductDependency;
package = F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */;
productName = Collections;
};
CCE5BF78B125320CBF3BB834 /* PostHog */ = { CCE5BF78B125320CBF3BB834 /* PostHog */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = 96495DD8554E2F39D3954354 /* XCRemoteSwiftPackageReference "posthog-ios" */; package = 96495DD8554E2F39D3954354 /* XCRemoteSwiftPackageReference "posthog-ios" */;
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
wasCreatedForAppExtension = "YES"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "19F0C845D67E9BEA4BE7133E"
BuildableName = "ShareExtension.appex"
BlueprintName = "ShareExtension"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
onlyGenerateCoverageForSpecifiedTargets = "NO">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "19F0C845D67E9BEA4BE7133E"
BuildableName = "ShareExtension.appex"
BlueprintName = "ShareExtension"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "YES"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "19F0C845D67E9BEA4BE7133E"
BuildableName = "ShareExtension.appex"
BlueprintName = "ShareExtension"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "19F0C845D67E9BEA4BE7133E"
BuildableName = "ShareExtension.appex"
BlueprintName = "ShareExtension"
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
</CommandLineArguments>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -235,6 +235,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
} else { } else {
handleAppRoute(.childEventOnRoomAlias(eventID: eventID, alias: alias)) handleAppRoute(.childEventOnRoomAlias(eventID: eventID, alias: alias))
} }
case .share:
guard isExternalURL else {
MXLog.error("Received unexpected internal share route")
break
}
handleAppRoute(route)
default: default:
break break
} }
@@ -8,7 +8,7 @@
import Foundation import Foundation
import MatrixRustSDK import MatrixRustSDK
enum AppRoute: Equatable { enum AppRoute: Equatable, Hashable {
/// The app's home screen. /// The app's home screen.
case roomList case roomList
/// A room, shown as the root of the stack (popping any child rooms). /// A room, shown as the root of the stack (popping any child rooms).
@@ -41,6 +41,8 @@ enum AppRoute: Equatable {
case settings case settings
/// The setting screen for key backup. /// The setting screen for key backup.
case chatBackupSettings case chatBackupSettings
/// An external share request e.g. from the ShareExtension
case share(ShareExtensionPayload)
} }
struct AppRouteURLParser { struct AppRouteURLParser {
@@ -48,6 +50,7 @@ struct AppRouteURLParser {
init(appSettings: AppSettings) { init(appSettings: AppSettings) {
urlParsers = [ urlParsers = [
AppGroupURLParser(),
MatrixPermalinkParser(), MatrixPermalinkParser(),
ElementWebURLParser(domains: appSettings.elementWebHosts), ElementWebURLParser(domains: appSettings.elementWebHosts),
ElementCallURLParser() ElementCallURLParser()
@@ -73,6 +76,30 @@ protocol URLParser {
func route(from url: URL) -> AppRoute? func route(from url: URL) -> AppRoute?
} }
struct AppGroupURLParser: URLParser {
func route(from url: URL) -> AppRoute? {
guard let scheme = url.scheme,
scheme == InfoPlistReader.app.appScheme,
url.pathComponents.last == ShareExtensionConstants.urlPath else {
return nil
}
guard let query = url.query(percentEncoded: false),
let queryData = query.data(using: .utf8) else {
MXLog.error("Failed processing share parameters")
return nil
}
do {
let payload = try JSONDecoder().decode(ShareExtensionPayload.self, from: queryData)
return .share(payload)
} catch {
MXLog.error("Failed decoding share payload with error: \(error)")
return nil
}
}
}
/// The parser for Element Call links. This always returns a `.genericCallLink`. /// The parser for Element Call links. This always returns a `.genericCallLink`.
struct ElementCallURLParser: URLParser { struct ElementCallURLParser: URLParser {
private let knownHosts = ["call.element.io"] private let knownHosts = ["call.element.io"]
@@ -83,7 +83,7 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
case .roomList, .room, .roomAlias, .childRoom, .childRoomAlias, case .roomList, .room, .roomAlias, .childRoom, .childRoomAlias,
.roomDetails, .roomMemberDetails, .userProfile, .roomDetails, .roomMemberDetails, .userProfile,
.event, .eventOnRoomAlias, .childEvent, .childEventOnRoomAlias, .event, .eventOnRoomAlias, .childEvent, .childEventOnRoomAlias,
.call, .genericCallLink, .settings: .call, .genericCallLink, .settings, .share:
// These routes aren't in this flow so clear the entire stack. // These routes aren't in this flow so clear the entire stack.
clearRoute(animated: animated) clearRoute(animated: animated)
case .chatBackupSettings: case .chatBackupSettings:
@@ -293,9 +293,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters) let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters)
coordinator.actions coordinator.actions
.sink { [weak self] action in .sink { action in
guard let self else { return }
switch action { switch action {
case .complete: case .complete:
break // Moving to next state is Handled by the global session verification listener break // Moving to next state is Handled by the global session verification listener
@@ -65,7 +65,9 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))
guard let timelineController = await roomTimelineControllerFactory.buildRoomPinnedTimelineController(roomProxy: roomProxy, timelineItemFactory: timelineItemFactory) else { guard let timelineController = await roomTimelineControllerFactory.buildRoomPinnedTimelineController(roomProxy: roomProxy,
timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider) else {
fatalError("This can never fail because we allow this view to be presented only when the timeline is fully loaded and not nil") fatalError("This can never fail because we allow this view to be presented only when the timeline is fully loaded and not nil")
} }
@@ -34,6 +34,8 @@ enum RoomFlowCoordinatorEntryPoint: Hashable {
case eventID(String) case eventID(String)
/// The flow will start by showing the room's details. /// The flow will start by showing the room's details.
case roomDetails case roomDetails
/// An external media share request
case share(ShareExtensionPayload)
var isEventID: Bool { var isEventID: Bool {
guard case .eventID = self else { return false } guard case .eventID = self else { return false }
@@ -41,6 +43,32 @@ enum RoomFlowCoordinatorEntryPoint: Hashable {
} }
} }
struct FocusEvent: Hashable {
/// The event ID that the timeline should be focussed around
let eventID: String
/// if the focus is coming from the pinned timeline, this should also update the pin banner
let shouldSetPin: Bool
}
private enum PinnedEventsTimelineSource: Hashable {
case room
case details(isRoot: Bool)
}
private enum PresentationAction: Hashable {
case eventFocus(FocusEvent)
case share(ShareExtensionPayload)
var focusedEvent: FocusEvent? {
switch self {
case .eventFocus(let focusEvent):
return focusEvent
default:
return nil
}
}
}
// swiftlint:disable:next type_body_length // swiftlint:disable:next type_body_length
class RoomFlowCoordinator: FlowCoordinatorProtocol { class RoomFlowCoordinator: FlowCoordinatorProtocol {
private let roomID: String private let roomID: String
@@ -112,6 +140,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
fatalError("This flow coordinator expect a route") fatalError("This flow coordinator expect a route")
} }
// swiftlint:disable:next cyclomatic_complexity
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) { func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
guard stateMachine.state != .complete else { guard stateMachine.state != .complete else {
fatalError("This flow coordinator is `finished` ☠️") fatalError("This flow coordinator is `finished` ☠️")
@@ -152,7 +181,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID), userInfo: EventUserInfo(animated: animated)) stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID), userInfo: EventUserInfo(animated: animated))
} }
case .event(let eventID, let roomID, let via): case .event(let eventID, let roomID, let via):
Task { await handleRoomRoute(roomID: roomID, via: via, focussedEventID: eventID, animated: animated) } Task {
await handleRoomRoute(roomID: roomID,
via: via,
presentationAction: .eventFocus(.init(eventID: eventID, shouldSetPin: false)),
animated: animated)
}
case .childEvent(let eventID, let roomID, let via): case .childEvent(let eventID, let roomID, let via):
if case .presentingChild = stateMachine.state, let childRoomFlowCoordinator { if case .presentingChild = stateMachine.state, let childRoomFlowCoordinator {
childRoomFlowCoordinator.handleAppRoute(appRoute, animated: animated) childRoomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
@@ -161,6 +195,21 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
} else { } else {
roomScreenCoordinator?.focusOnEvent(.init(eventID: eventID, shouldSetPin: false)) roomScreenCoordinator?.focusOnEvent(.init(eventID: eventID, shouldSetPin: false))
} }
case .share(let payload):
guard case let .mediaFile(roomID, _) = payload else {
return
}
guard let roomID, roomID == self.roomID else {
fatalError("Navigation route doesn't belong to this room flow.")
}
Task {
await handleRoomRoute(roomID: roomID,
via: [],
presentationAction: .share(payload),
animated: animated)
}
case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias: case .roomAlias, .childRoomAlias, .eventOnRoomAlias, .childEventOnRoomAlias:
break // These are converted to a room ID route one level above. break // These are converted to a room ID route one level above.
case .roomList, .userProfile, .call, .genericCallLink, .settings, .chatBackupSettings: case .roomList, .userProfile, .call, .genericCallLink, .settings, .chatBackupSettings:
@@ -176,7 +225,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
actionsSubject.send(.presentCallScreen(roomProxy: roomProxy)) actionsSubject.send(.presentCallScreen(roomProxy: roomProxy))
} }
private func handleRoomRoute(roomID: String, via: [String], focussedEventID: String? = nil, animated: Bool) async { private func handleRoomRoute(roomID: String, via: [String], presentationAction: PresentationAction? = nil, animated: Bool) async {
guard roomID == self.roomID else { fatalError("Navigation route doesn't belong to this room flow.") } guard roomID == self.roomID else { fatalError("Navigation route doesn't belong to this room flow.") }
guard let room = await userSession.clientProxy.roomForIdentifier(roomID) else { guard let room = await userSession.clientProxy.roomForIdentifier(roomID) else {
@@ -187,8 +236,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
switch room { switch room {
case .joined(let roomProxy): case .joined(let roomProxy):
await storeAndSubscribeToRoomProxy(roomProxy) await storeAndSubscribeToRoomProxy(roomProxy)
let focussedEvent = focussedEventID.map { FocusEvent(eventID: $0, shouldSetPin: false) } stateMachine.tryEvent(.presentRoom(presentationAction: presentationAction), userInfo: EventUserInfo(animated: animated))
stateMachine.tryEvent(.presentRoom(focussedEvent: focussedEvent), userInfo: EventUserInfo(animated: animated))
default: default:
stateMachine.tryEvent(.presentJoinRoomScreen(via: via), userInfo: EventUserInfo(animated: animated)) stateMachine.tryEvent(.presentJoinRoomScreen(via: via), userInfo: EventUserInfo(animated: animated))
} }
@@ -377,8 +425,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
case (_, .dismissJoinRoomScreen, .complete): case (_, .dismissJoinRoomScreen, .complete):
dismissFlow(animated: animated) dismissFlow(animated: animated)
case (_, .presentRoom(let focussedEvent), .room): case (_, .presentRoom(let presentationAction), .room):
Task { await self.presentRoom(fromState: context.fromState, focussedEvent: focussedEvent, animated: animated) } Task {
await self.presentRoom(fromState: context.fromState,
presentationAction: presentationAction,
animated: animated)
}
case (_, .dismissFlow, .complete): case (_, .dismissFlow, .complete):
dismissFlow(animated: animated) dismissFlow(animated: animated)
@@ -445,9 +497,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
break break
case (.mediaUploadPicker, .presentMediaUploadPreview, .mediaUploadPreview(let fileURL)): case (.mediaUploadPicker, .presentMediaUploadPreview, .mediaUploadPreview(let fileURL)):
presentMediaUploadPreviewScreen(for: fileURL) presentMediaUploadPreviewScreen(for: fileURL, animated: animated)
case (.room, .presentMediaUploadPreview, .mediaUploadPreview(let fileURL)): case (.room, .presentMediaUploadPreview, .mediaUploadPreview(let fileURL)):
presentMediaUploadPreviewScreen(for: fileURL) presentMediaUploadPreviewScreen(for: fileURL, animated: animated)
case (.mediaUploadPreview, .dismissMediaUploadPreview, .room): case (.mediaUploadPreview, .dismissMediaUploadPreview, .room):
break break
@@ -542,7 +594,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
/// - fromState: The state that asked for the room presentation. /// - fromState: The state that asked for the room presentation.
/// - focussedEvent: An (optional) struct that contains the event ID that the timeline should be focussed around, and a boolean telling if such event should update the pinned events banner /// - focussedEvent: An (optional) struct that contains the event ID that the timeline should be focussed around, and a boolean telling if such event should update the pinned events banner
/// - animated: whether it should animate the transition /// - animated: whether it should animate the transition
private func presentRoom(fromState: State, focussedEvent: FocusEvent?, animated: Bool) async { private func presentRoom(fromState: State, presentationAction: PresentationAction?, animated: Bool) async {
// If any sheets are presented dismiss them, rely on their dismissal callbacks to transition the state machine // If any sheets are presented dismiss them, rely on their dismissal callbacks to transition the state machine
// through the correct states before presenting the room // through the correct states before presenting the room
navigationStackCoordinator.setSheetCoordinator(nil) navigationStackCoordinator.setSheetCoordinator(nil)
@@ -559,9 +611,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
default: default:
// The room is already on the stack, no need to present it again // The room is already on the stack, no need to present it again
// Check if we need to focus on an event switch presentationAction {
if let focussedEvent { case .eventFocus(let focusedEvent):
roomScreenCoordinator?.focusOnEvent(focussedEvent) roomScreenCoordinator?.focusOnEvent(focusedEvent)
case .share(.mediaFile(_, let mediaFile)):
stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: mediaFile.url))
default:
break
} }
return return
@@ -580,8 +636,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID)) stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))
let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy, let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy,
initialFocussedEventID: focussedEvent?.eventID, initialFocussedEventID: presentationAction?.focusedEvent?.eventID,
timelineItemFactory: timelineItemFactory) timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider)
self.timelineController = timelineController self.timelineController = timelineController
analytics.trackViewRoom(isDM: roomProxy.infoPublisher.value.isDirect, isSpace: roomProxy.infoPublisher.value.isSpace) analytics.trackViewRoom(isDM: roomProxy.infoPublisher.value.isDirect, isSpace: roomProxy.infoPublisher.value.isSpace)
@@ -592,7 +649,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let parameters = RoomScreenCoordinatorParameters(clientProxy: userSession.clientProxy, let parameters = RoomScreenCoordinatorParameters(clientProxy: userSession.clientProxy,
roomProxy: roomProxy, roomProxy: roomProxy,
focussedEvent: focussedEvent, focussedEvent: presentationAction?.focusedEvent,
timelineController: timelineController, timelineController: timelineController,
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
mediaPlayerProvider: MediaPlayerProvider(), mediaPlayerProvider: MediaPlayerProvider(),
@@ -655,6 +712,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
self?.stateMachine.tryEvent(.dismissFlow) self?.stateMachine.tryEvent(.dismissFlow)
} }
} }
switch presentationAction {
case .share(.mediaFile(_, let mediaFile)):
stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: mediaFile.url), userInfo: EventUserInfo(animated: animated))
default:
break
}
} }
private func presentJoinRoomScreen(via: [String], animated: Bool) { private func presentJoinRoomScreen(via: [String], animated: Bool) {
@@ -679,7 +743,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) { if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) {
await storeAndSubscribeToRoomProxy(roomProxy) await storeAndSubscribeToRoomProxy(roomProxy)
stateMachine.tryEvent(.presentRoom(focussedEvent: nil), userInfo: EventUserInfo(animated: animated)) stateMachine.tryEvent(.presentRoom(presentationAction: nil), userInfo: EventUserInfo(animated: animated))
analytics.trackJoinedRoom(isDM: roomProxy.infoPublisher.value.isDirect, analytics.trackJoinedRoom(isDM: roomProxy.infoPublisher.value.isDirect,
isSpace: roomProxy.infoPublisher.value.isSpace, isSpace: roomProxy.infoPublisher.value.isSpace,
@@ -894,7 +958,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
} }
} }
private func presentMediaUploadPreviewScreen(for url: URL) { private func presentMediaUploadPreviewScreen(for url: URL, animated: Bool) {
let stackCoordinator = NavigationStackCoordinator() let stackCoordinator = NavigationStackCoordinator()
let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController, let parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController,
@@ -918,7 +982,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator) stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator)
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in navigationStackCoordinator.setSheetCoordinator(stackCoordinator, animated: animated) { [weak self] in
self?.stateMachine.tryEvent(.dismissMediaUploadPreview) self?.stateMachine.tryEvent(.dismissMediaUploadPreview)
} }
} }
@@ -1091,7 +1155,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let roomTimelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy, let roomTimelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy,
initialFocussedEventID: nil, initialFocussedEventID: nil,
timelineItemFactory: timelineItemFactory) timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider)
let parameters = RoomPollsHistoryScreenCoordinatorParameters(pollInteractionHandler: PollInteractionHandler(analyticsService: analytics, roomProxy: roomProxy), let parameters = RoomPollsHistoryScreenCoordinatorParameters(pollInteractionHandler: PollInteractionHandler(analyticsService: analytics, roomProxy: roomProxy),
roomTimelineController: roomTimelineController) roomTimelineController: roomTimelineController)
@@ -1361,7 +1426,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
stateMachine.tryEvent(.startChildFlow(roomID: roomID, via: [], entryPoint: .room)) stateMachine.tryEvent(.startChildFlow(roomID: roomID, via: [], entryPoint: .room))
case .displayRoomScreenWithFocussedPin(let eventID): case .displayRoomScreenWithFocussedPin(let eventID):
navigationStackCoordinator.setSheetCoordinator(nil) navigationStackCoordinator.setSheetCoordinator(nil)
stateMachine.tryEvent(.presentRoom(focussedEvent: .init(eventID: eventID, shouldSetPin: true))) stateMachine.tryEvent(.presentRoom(presentationAction: .eventFocus(.init(eventID: eventID, shouldSetPin: true))))
} }
} }
.store(in: &cancellables) .store(in: &cancellables)
@@ -1427,6 +1492,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
coordinator.handleAppRoute(.event(eventID: eventID, roomID: roomID, via: via), animated: true) coordinator.handleAppRoute(.event(eventID: eventID, roomID: roomID, via: via), animated: true)
case .roomDetails: case .roomDetails:
coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true) coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: true)
case .share(let payload):
coordinator.handleAppRoute(.share(payload), animated: true)
} }
} }
} }
@@ -1483,7 +1550,7 @@ private extension RoomFlowCoordinator {
case presentJoinRoomScreen(via: [String]) case presentJoinRoomScreen(via: [String])
case dismissJoinRoomScreen case dismissJoinRoomScreen
case presentRoom(focussedEvent: FocusEvent?) case presentRoom(presentationAction: PresentationAction?)
case dismissFlow case dismissFlow
case presentReportContent(itemID: TimelineItemIdentifier, senderID: String) case presentReportContent(itemID: TimelineItemIdentifier, senderID: String)
@@ -1559,15 +1626,3 @@ private extension Result {
} }
} }
} }
private enum PinnedEventsTimelineSource: Hashable {
case room
case details(isRoot: Bool)
}
struct FocusEvent: Hashable {
/// The event ID that the timeline should be focussed around
let eventID: String
/// if the focus is coming from the pinned timeline, this should also update the pin banner
let shouldSetPin: Bool
}
@@ -206,6 +206,18 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
presentCallScreen(genericCallLink: url) presentCallScreen(genericCallLink: url)
case .settings, .chatBackupSettings: case .settings, .chatBackupSettings:
settingsFlowCoordinator.handleAppRoute(appRoute, animated: animated) settingsFlowCoordinator.handleAppRoute(appRoute, animated: animated)
case .share(let payload):
switch payload {
case .mediaFile(let roomID, _):
if let roomID {
stateMachine.processEvent(.selectRoom(roomID: roomID,
via: [],
entryPoint: .share(payload)),
userInfo: .init(animated: animated))
} else {
stateMachine.processEvent(.showShareExtensionRoomList(sharePayload: payload), userInfo: .init(animated: animated))
}
}
} }
} }
@@ -243,6 +255,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
case .room: .room(roomID: roomID, via: via) case .room: .room(roomID: roomID, via: via)
case .roomDetails: .roomDetails(roomID: roomID) case .roomDetails: .roomDetails(roomID: roomID)
case .eventID(let eventID): .event(eventID: eventID, roomID: roomID, via: via) // ignored. case .eventID(let eventID): .event(eventID: eventID, roomID: roomID, via: via) // ignored.
case .share(let payload): .share(payload)
} }
roomFlowCoordinator.handleAppRoute(route, animated: animated) roomFlowCoordinator.handleAppRoute(route, animated: animated)
} else { } else {
@@ -296,6 +309,12 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
case (.userProfileScreen, .dismissedUserProfileScreen, .roomList): case (.userProfileScreen, .dismissedUserProfileScreen, .roomList):
break break
case (.roomList, .showShareExtensionRoomList, .shareExtensionRoomList(let sharePayload)):
clearRoute(animated: animated)
presentRoomSelectionScreen(sharePayload: sharePayload, animated: animated)
case (.shareExtensionRoomList, .dismissedShareExtensionRoomList, .roomList):
dismissRoomSelectionScreen()
default: default:
fatalError("Unknown transition: \(context)") fatalError("Unknown transition: \(context)")
} }
@@ -583,6 +602,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
coordinator.handleAppRoute(.event(eventID: eventID, roomID: roomID, via: via), animated: animated) coordinator.handleAppRoute(.event(eventID: eventID, roomID: roomID, via: via), animated: animated)
case .roomDetails: case .roomDetails:
coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: animated) coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: animated)
case .share(let payload):
coordinator.handleAppRoute(.share(payload), animated: animated)
} }
Task { Task {
@@ -894,6 +915,52 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
} }
} }
// MARK: Sharing
private func presentRoomSelectionScreen(sharePayload: ShareExtensionPayload, animated: Bool) {
guard let roomSummaryProvider = userSession.clientProxy.alternateRoomSummaryProvider else {
fatalError()
}
let stackCoordinator = NavigationStackCoordinator()
let coordinator = RoomSelectionScreenCoordinator(parameters: .init(clientProxy: userSession.clientProxy,
roomSummaryProvider: roomSummaryProvider,
mediaProvider: userSession.mediaProvider))
coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { return }
switch action {
case .dismiss:
navigationSplitCoordinator.setSheetCoordinator(nil)
case .confirm(let roomID):
let sharePayload = switch sharePayload {
case .mediaFile(_, let mediaFile):
ShareExtensionPayload.mediaFile(roomID: roomID, mediaFile: mediaFile)
}
navigationSplitCoordinator.setSheetCoordinator(nil)
stateMachine.processEvent(.selectRoom(roomID: roomID,
via: [],
entryPoint: .share(sharePayload)),
userInfo: .init(animated: animated))
}
}
.store(in: &cancellables)
stackCoordinator.setRootCoordinator(coordinator)
navigationSplitCoordinator.setSheetCoordinator(stackCoordinator, animated: animated) { [weak self] in
self?.stateMachine.processEvent(.dismissedShareExtensionRoomList)
}
}
private func dismissRoomSelectionScreen() {
navigationSplitCoordinator.setSheetCoordinator(nil)
}
// MARK: Toasts and loading indicators // MARK: Toasts and loading indicators
private static let loadingIndicatorIdentifier = "\(UserSessionFlowCoordinator.self)-Loading" private static let loadingIndicatorIdentifier = "\(UserSessionFlowCoordinator.self)-Loading"
@@ -42,10 +42,12 @@ class UserSessionFlowCoordinatorStateMachine {
/// Showing the user profile screen. This screen clears the navigation. /// Showing the user profile screen. This screen clears the navigation.
case userProfileScreen case userProfileScreen
case shareExtensionRoomList(sharePayload: ShareExtensionPayload)
/// The selected room ID from the state if available. /// The selected room ID from the state if available.
var selectedRoomID: String? { var selectedRoomID: String? {
switch self { switch self {
case .initial, .userProfileScreen: case .initial, .userProfileScreen, .shareExtensionRoomList:
nil nil
case .roomList(let selectedRoomID), case .roomList(let selectedRoomID),
.feedbackScreen(let selectedRoomID), .feedbackScreen(let selectedRoomID),
@@ -116,6 +118,9 @@ class UserSessionFlowCoordinatorStateMachine {
case showUserProfileScreen(userID: String) case showUserProfileScreen(userID: String)
/// The user profile screen has been dismissed. /// The user profile screen has been dismissed.
case dismissedUserProfileScreen case dismissedUserProfileScreen
case showShareExtensionRoomList(sharePayload: ShareExtensionPayload)
case dismissedShareExtensionRoomList
} }
private let stateMachine: StateMachine<State, Event> private let stateMachine: StateMachine<State, Event>
@@ -184,6 +189,11 @@ class UserSessionFlowCoordinatorStateMachine {
case (.userProfileScreen, .dismissedUserProfileScreen): case (.userProfileScreen, .dismissedUserProfileScreen):
return .roomList(selectedRoomID: nil) return .roomList(selectedRoomID: nil)
case (.roomList, .showShareExtensionRoomList(let sharePayload)):
return .shareExtensionRoomList(sharePayload: sharePayload)
case (.shareExtensionRoomList, .dismissedShareExtensionRoomList):
return .roomList(selectedRoomID: nil)
default: default:
return nil return nil
} }
@@ -12939,15 +12939,15 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
//MARK: - buildRoomTimelineController //MARK: - buildRoomTimelineController
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount = 0 var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount: Int { var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount: Int {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount
} else { } else {
var returnValue: Int? = nil var returnValue: Int? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount
} }
return returnValue! return returnValue!
@@ -12955,29 +12955,29 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount = newValue buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount = newValue buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
} }
} }
} }
} }
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCalled: Bool { var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCalled: Bool {
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount > 0 return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount > 0
} }
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol)? var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)?
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol)] = [] var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = []
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue: RoomTimelineControllerProtocol! var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue: RoomTimelineControllerProtocol!
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReturnValue: RoomTimelineControllerProtocol! { var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue: RoomTimelineControllerProtocol! {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue
} else { } else {
var returnValue: RoomTimelineControllerProtocol? = nil var returnValue: RoomTimelineControllerProtocol? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue
} }
return returnValue! return returnValue!
@@ -12985,39 +12985,39 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue = newValue buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue = newValue buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue
} }
} }
} }
} }
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryClosure: ((JoinedRoomProxyProtocol, String?, RoomTimelineItemFactoryProtocol) -> RoomTimelineControllerProtocol)? var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure: ((JoinedRoomProxyProtocol, String?, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) -> RoomTimelineControllerProtocol)?
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol) -> RoomTimelineControllerProtocol { func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol {
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount += 1 buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount += 1
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments = (roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory) buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)
DispatchQueue.main.async { DispatchQueue.main.async {
self.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedInvocations.append((roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory)) self.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider))
} }
if let buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryClosure = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryClosure { if let buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure {
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryClosure(roomProxy, initialFocussedEventID, timelineItemFactory) return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure(roomProxy, initialFocussedEventID, timelineItemFactory, mediaProvider)
} else { } else {
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReturnValue return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue
} }
} }
//MARK: - buildRoomPinnedTimelineController //MARK: - buildRoomPinnedTimelineController
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount = 0 var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCallsCount: Int { var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount: Int {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount
} else { } else {
var returnValue: Int? = nil var returnValue: Int? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount
} }
return returnValue! return returnValue!
@@ -13025,29 +13025,29 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount = newValue buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount = newValue buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
} }
} }
} }
} }
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCalled: Bool { var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCalled: Bool {
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCallsCount > 0 return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount > 0
} }
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol)? var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)?
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol)] = [] var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = []
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue: RoomTimelineControllerProtocol? var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue: RoomTimelineControllerProtocol?
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReturnValue: RoomTimelineControllerProtocol? { var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue: RoomTimelineControllerProtocol? {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue
} else { } else {
var returnValue: RoomTimelineControllerProtocol?? = nil var returnValue: RoomTimelineControllerProtocol?? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue
} }
return returnValue! return returnValue!
@@ -13055,26 +13055,26 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue = newValue buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue = newValue buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue
} }
} }
} }
} }
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryClosure: ((JoinedRoomProxyProtocol, RoomTimelineItemFactoryProtocol) async -> RoomTimelineControllerProtocol?)? var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure: ((JoinedRoomProxyProtocol, RoomTimelineItemFactoryProtocol, MediaProviderProtocol) async -> RoomTimelineControllerProtocol?)?
func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol) async -> RoomTimelineControllerProtocol? { func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? {
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCallsCount += 1 buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount += 1
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedArguments = (roomProxy: roomProxy, timelineItemFactory: timelineItemFactory) buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)
DispatchQueue.main.async { DispatchQueue.main.async {
self.buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedInvocations.append((roomProxy: roomProxy, timelineItemFactory: timelineItemFactory)) self.buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider))
} }
if let buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryClosure = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryClosure { if let buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure {
return await buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryClosure(roomProxy, timelineItemFactory) return await buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure(roomProxy, timelineItemFactory, mediaProvider)
} else { } else {
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReturnValue return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue
} }
} }
} }
@@ -15,7 +15,7 @@ extension RoomTimelineControllerFactoryMock {
convenience init(configuration: RoomTimelineControllerFactoryMockConfiguration) { convenience init(configuration: RoomTimelineControllerFactoryMockConfiguration) {
self.init() self.init()
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReturnValue = configuration.timelineController ?? { buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue = configuration.timelineController ?? {
let timelineController = MockRoomTimelineController() let timelineController = MockRoomTimelineController()
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
return timelineController return timelineController
@@ -6,29 +6,55 @@
// //
import Foundation import Foundation
import UIKit import SwiftUI
enum AvatarSize { enum Avatars {
case user(on: UserAvatarSizeOnScreen) enum Size {
case room(on: RoomAvatarSizeOnScreen) case user(on: UserAvatarSizeOnScreen)
// custom case room(on: RoomAvatarSizeOnScreen)
case custom(CGFloat) // custom
case custom(CGFloat)
/// Value in UIKit points /// Value in UIKit points
var value: CGFloat { var value: CGFloat {
switch self { switch self {
case .user(let screen): case .user(let screen):
return screen.value return screen.value
case .room(let screen): case .room(let screen):
return screen.value return screen.value
case .custom(let val): case .custom(let val):
return val return val
}
}
/// Value in pixels by using the scale of the main screen
var scaledValue: CGFloat {
value * UIScreen.main.scale
}
var scaledSize: CGSize {
CGSize(width: scaledValue, height: scaledValue)
} }
} }
/// Value in pixels by using the scale of the main screen @MainActor
var scaledValue: CGFloat { static func generatePlaceholderAvatarImageData(name: String, id: String, size: CGSize) -> Data? {
value * UIScreen.main.scale let image = PlaceholderAvatarImage(name: name, contentID: id)
.clipShape(Circle())
.frame(width: size.width, height: size.height)
let renderer = ImageRenderer(content: image)
// Specify the scale so the image is rendered correctly. We don't have access to the screen
// here so a hardcoded 3.0 will have to do
renderer.scale = 3.0
guard let image = renderer.uiImage else {
MXLog.info("Generating notification icon placeholder failed")
return nil
}
return image.pngData()
} }
} }
@@ -87,6 +113,7 @@ enum RoomAvatarSizeOnScreen {
case home case home
case messageForwarding case messageForwarding
case globalSearch case globalSearch
case roomSelection
case details case details
case notificationSettings case notificationSettings
case roomDirectorySearch case roomDirectorySearch
@@ -104,6 +131,8 @@ enum RoomAvatarSizeOnScreen {
return 36 return 36
case .globalSearch: case .globalSearch:
return 36 return 36
case .roomSelection:
return 36
case .home: case .home:
return 52 return 52
case .details: case .details:
@@ -113,9 +142,3 @@ enum RoomAvatarSizeOnScreen {
} }
} }
} }
extension AvatarSize {
var scaledSize: CGSize {
CGSize(width: scaledValue, height: scaledValue)
}
}
@@ -38,7 +38,7 @@ extension FileManager {
@discardableResult @discardableResult
func writeDataToTemporaryDirectory(data: Data, fileName: String) throws -> URL { func writeDataToTemporaryDirectory(data: Data, fileName: String) throws -> URL {
let newURL = URL.temporaryDirectory.appendingPathComponent(fileName) let newURL = URL.appGroupTemporaryDirectory.appendingPathComponent(fileName)
try data.write(to: newURL) try data.write(to: newURL)
@@ -183,8 +183,8 @@ extension UNMutableNotificationContent {
@MainActor @MainActor
private func getPlaceholderAvatarImageData(name: String, id: String) async -> Data? { private func getPlaceholderAvatarImageData(name: String, id: String) async -> Data? {
// The version value is used in case the design of the placeholder is updated to force a replacement // The version value is used in case the design of the placeholder is updated to force a replacement
let shouldFlipAvatar = shouldFlipAvatar() let prefix = "notification_placeholderV9"
let prefix = "notification_placeholder\(shouldFlipAvatar ? "V9F" : "V9")"
let fileName = "\(prefix)_\(name)_\(id).png" let fileName = "\(prefix)_\(name)_\(id).png"
if let data = try? Data(contentsOf: URL.temporaryDirectory.appendingPathComponent(fileName)) { if let data = try? Data(contentsOf: URL.temporaryDirectory.appendingPathComponent(fileName)) {
MXLog.info("Found existing notification icon placeholder") MXLog.info("Found existing notification icon placeholder")
@@ -192,28 +192,8 @@ extension UNMutableNotificationContent {
} }
MXLog.info("Generating notification icon placeholder") MXLog.info("Generating notification icon placeholder")
let image = PlaceholderAvatarImage(name: name,
contentID: id)
.clipShape(Circle())
.frame(width: 50, height: 50)
let renderer = ImageRenderer(content: image)
// Specify the scale so the image is rendered correctly. We don't have access to the screen let data = Avatars.generatePlaceholderAvatarImageData(name: name, id: id, size: .init(width: 50, height: 50))
// here so a hardcoded 3.0 will have to do
renderer.scale = 3.0
guard let image = renderer.uiImage else {
MXLog.info("Generating notification icon placeholder failed")
return nil
}
let data: Data?
if shouldFlipAvatar {
data = image.flippedVertically().pngData()
} else {
data = image.pngData()
}
if let data { if let data {
do { do {
@@ -224,44 +204,7 @@ extension UNMutableNotificationContent {
return data return data
} }
} }
return data return data
} }
/// On simulators and macOS the image is rendered correctly
/// On devices before iOS 17 and iOS 17.2.0 it's rendered upside down and needs to be flipped
/// On all other versions it's rendered correctly and **doesn't** need to be flipped
private func shouldFlipAvatar() -> Bool {
#if targetEnvironment(simulator)
return false
#else
if ProcessInfo.processInfo.isiOSAppOnMac {
return false
}
guard let version = Version(UIDevice.current.systemVersion) else {
return false
}
if version < Version(17, 0, 0) {
return true
}
if version == Version(17, 2, 0) {
return true
}
return false
#endif
}
}
private extension UIImage {
func flippedVertically() -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
return UIGraphicsImageRenderer(size: size, format: format).image { context in
context.cgContext.concatenate(CGAffineTransform(scaleX: 1, y: -1))
self.draw(at: CGPoint(x: 0, y: -size.height))
}
}
} }
+15 -2
View File
@@ -60,7 +60,7 @@ extension URL: @retroactive ExpressibleByStringLiteral {
} }
/// The base directory where all application support data is stored. /// The base directory where all application support data is stored.
static var cachesBaseDirectory: URL { static var sessionCachesBaseDirectory: URL {
let url = appGroupContainerDirectory let url = appGroupContainerDirectory
.appendingPathComponent("Library", isDirectory: true) .appendingPathComponent("Library", isDirectory: true)
.appendingPathComponent("Caches", isDirectory: true) .appendingPathComponent("Caches", isDirectory: true)
@@ -69,7 +69,20 @@ extension URL: @retroactive ExpressibleByStringLiteral {
try? FileManager.default.createDirectoryIfNeeded(at: url) try? FileManager.default.createDirectoryIfNeeded(at: url)
// Caches are excluded from backups automatically anyway. // Caches are excluded from backups automatically.
// https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
return url
}
/// The app group temporary directory
static var appGroupTemporaryDirectory: URL {
let url = appGroupContainerDirectory
.appendingPathComponent("tmp", isDirectory: true)
try? FileManager.default.createDirectoryIfNeeded(at: url)
// Temporary files are excluded from backups automatically.
// https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html // https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
return url return url
@@ -8,7 +8,7 @@
import Foundation import Foundation
import MatrixRustSDK import MatrixRustSDK
enum NSELogger { enum ExtensionLogger {
private static var isConfigured = false private static var isConfigured = false
/// Memory formatter, uses exact 2 fraction digits and no grouping /// Memory formatter, uses exact 2 fraction digits and no grouping
@@ -66,13 +66,13 @@ enum NSELogger {
return "\(formattedStr) MB" return "\(formattedStr) MB"
} }
static func configure(logLevel: TracingConfiguration.LogLevel) { static func configure(currentTarget: String, logLevel: TracingConfiguration.LogLevel) {
guard !isConfigured else { guard !isConfigured else {
return return
} }
isConfigured = true isConfigured = true
MXLog.configure(currentTarget: "nse", filePrefix: "nse", logLevel: logLevel) MXLog.configure(currentTarget: currentTarget, filePrefix: currentTarget, logLevel: logLevel)
} }
static func logMemory(with tag: String) { static func logMemory(with tag: String) {
@@ -25,13 +25,13 @@ struct AvatarHeaderView<Footer: View>: View {
private let subtitle: String? private let subtitle: String?
private let badges: [Badge] private let badges: [Badge]
private let avatarSize: AvatarSize private let avatarSize: Avatars.Size
private let mediaProvider: MediaProviderProtocol? private let mediaProvider: MediaProviderProtocol?
private var onAvatarTap: ((URL) -> Void)? private var onAvatarTap: ((URL) -> Void)?
@ViewBuilder private var footer: () -> Footer @ViewBuilder private var footer: () -> Footer
init(room: RoomDetails, init(room: RoomDetails,
avatarSize: AvatarSize, avatarSize: Avatars.Size,
mediaProvider: MediaProviderProtocol? = nil, mediaProvider: MediaProviderProtocol? = nil,
onAvatarTap: ((URL) -> Void)? = nil, onAvatarTap: ((URL) -> Void)? = nil,
@ViewBuilder footer: @escaping () -> Footer) { @ViewBuilder footer: @escaping () -> Footer) {
@@ -72,7 +72,7 @@ struct AvatarHeaderView<Footer: View>: View {
init(member: RoomMemberDetails, init(member: RoomMemberDetails,
isVerified: Bool = false, isVerified: Bool = false,
avatarSize: AvatarSize, avatarSize: Avatars.Size,
mediaProvider: MediaProviderProtocol? = nil, mediaProvider: MediaProviderProtocol? = nil,
onAvatarTap: ((URL) -> Void)? = nil, onAvatarTap: ((URL) -> Void)? = nil,
@ViewBuilder footer: @escaping () -> Footer) { @ViewBuilder footer: @escaping () -> Footer) {
@@ -88,7 +88,7 @@ struct AvatarHeaderView<Footer: View>: View {
init(user: UserProfileProxy, init(user: UserProfileProxy,
isVerified: Bool, isVerified: Bool,
avatarSize: AvatarSize, avatarSize: Avatars.Size,
mediaProvider: MediaProviderProtocol? = nil, mediaProvider: MediaProviderProtocol? = nil,
onAvatarTap: ((URL) -> Void)? = nil, onAvatarTap: ((URL) -> Void)? = nil,
@ViewBuilder footer: @escaping () -> Footer) { @ViewBuilder footer: @escaping () -> Footer) {
@@ -11,7 +11,7 @@ struct LoadableAvatarImage: View {
private let url: URL? private let url: URL?
private let name: String? private let name: String?
private let contentID: String? private let contentID: String?
private let avatarSize: AvatarSize private let avatarSize: Avatars.Size
private let mediaProvider: MediaProviderProtocol? private let mediaProvider: MediaProviderProtocol?
private let onTap: ((URL) -> Void)? private let onTap: ((URL) -> Void)?
@@ -19,7 +19,7 @@ struct LoadableAvatarImage: View {
init(url: URL?, name: String?, init(url: URL?, name: String?,
contentID: String?, contentID: String?,
avatarSize: AvatarSize, avatarSize: Avatars.Size,
mediaProvider: MediaProviderProtocol?, mediaProvider: MediaProviderProtocol?,
onTap: ((URL) -> Void)? = nil) { onTap: ((URL) -> Void)? = nil) {
self.url = url self.url = url
@@ -12,12 +12,12 @@ struct OverridableAvatarImage: View {
private let url: URL? private let url: URL?
private let name: String? private let name: String?
private let contentID: String? private let contentID: String?
private let avatarSize: AvatarSize private let avatarSize: Avatars.Size
private let mediaProvider: MediaProviderProtocol? private let mediaProvider: MediaProviderProtocol?
@ScaledMetric private var frameSize: CGFloat @ScaledMetric private var frameSize: CGFloat
init(overrideURL: URL?, url: URL?, name: String?, contentID: String?, avatarSize: AvatarSize, mediaProvider: MediaProviderProtocol?) { init(overrideURL: URL?, url: URL?, name: String?, contentID: String?, avatarSize: Avatars.Size, mediaProvider: MediaProviderProtocol?) {
self.overrideURL = overrideURL self.overrideURL = overrideURL
self.url = url self.url = url
self.name = name self.name = name
@@ -22,7 +22,7 @@ enum RoomAvatar: Equatable {
struct RoomAvatarImage: View { struct RoomAvatarImage: View {
let avatar: RoomAvatar let avatar: RoomAvatar
let avatarSize: AvatarSize let avatarSize: Avatars.Size
let mediaProvider: MediaProviderProtocol? let mediaProvider: MediaProviderProtocol?
private(set) var onAvatarTap: ((URL) -> Void)? private(set) var onAvatarTap: ((URL) -> Void)?
@@ -18,7 +18,7 @@ struct StackedAvatarsView: View {
let lineWidth: CGFloat let lineWidth: CGFloat
var shouldStackFromLast = false var shouldStackFromLast = false
let avatars: [StackedAvatarInfo] let avatars: [StackedAvatarInfo]
let avatarSize: AvatarSize let avatarSize: Avatars.Size
let mediaProvider: MediaProviderProtocol? let mediaProvider: MediaProviderProtocol?
var body: some View { var body: some View {
@@ -30,7 +30,7 @@ enum GlobalSearchScreenViewAction {
struct GlobalSearchRoom: Identifiable, Equatable { struct GlobalSearchRoom: Identifiable, Equatable {
let id: String let id: String
let name: String let title: String
let alias: String? let description: String
let avatar: RoomAvatar let avatar: RoomAvatar
} }
@@ -36,7 +36,11 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
.map(\.bindings.searchQuery) .map(\.bindings.searchQuery)
.removeDuplicates() .removeDuplicates()
.sink { [weak self] searchQuery in .sink { [weak self] searchQuery in
self?.roomSummaryProvider.setFilter(.search(query: searchQuery)) if searchQuery.isEmpty {
self?.roomSummaryProvider.setFilter(.all(filters: []))
} else {
self?.roomSummaryProvider.setFilter(.search(query: searchQuery))
}
} }
.store(in: &cancellables) .store(in: &cancellables)
@@ -66,8 +70,8 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
private func updateRooms(with summaries: [RoomSummary]) { private func updateRooms(with summaries: [RoomSummary]) {
state.rooms = summaries.compactMap { summary in state.rooms = summaries.compactMap { summary in
GlobalSearchRoom(id: summary.id, GlobalSearchRoom(id: summary.id,
name: summary.name, title: summary.name,
alias: summary.canonicalAlias, description: summary.roomListDescription,
avatar: summary.avatar) avatar: summary.avatar)
} }
} }
@@ -16,8 +16,8 @@ struct GlobalSearchScreenListRow: View {
var body: some View { var body: some View {
ZStack { // The list row swallows listRowBackgrounds for some reason ZStack { // The list row swallows listRowBackgrounds for some reason
ListRow(label: .avatar(title: room.name, ListRow(label: .avatar(title: room.title,
description: room.alias ?? room.id, description: room.description,
icon: avatar), icon: avatar),
kind: .label) kind: .label)
} }
@@ -42,8 +42,8 @@ struct GlobalSearchScreenListRow_Previews: PreviewProvider, TestablePreview {
static var previews: some View { static var previews: some View {
List { List {
GlobalSearchScreenListRow(room: .init(id: "123", GlobalSearchScreenListRow(room: .init(id: "123",
name: "Tech central", title: "Tech central",
alias: "The best place in the whole wide world", description: "The best place in the whole wide world",
avatar: .room(id: "123", avatar: .room(id: "123",
name: "Tech central", name: "Tech central",
avatarURL: .picturesDirectory)), avatarURL: .picturesDirectory)),
@@ -33,8 +33,8 @@ enum MessageForwardingScreenViewAction {
struct MessageForwardingRoom: Identifiable, Equatable { struct MessageForwardingRoom: Identifiable, Equatable {
let id: String let id: String
let name: String let title: String
let alias: String? let description: String
let avatar: RoomAvatar let avatar: RoomAvatar
} }
@@ -45,8 +45,11 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
.map(\.bindings.searchQuery) .map(\.bindings.searchQuery)
.removeDuplicates() .removeDuplicates()
.sink { [weak self] searchQuery in .sink { [weak self] searchQuery in
guard let self else { return } if searchQuery.isEmpty {
self.roomSummaryProvider.setFilter(.search(query: searchQuery)) self?.roomSummaryProvider.setFilter(.all(filters: []))
} else {
self?.roomSummaryProvider.setFilter(.search(query: searchQuery))
}
} }
.store(in: &cancellables) .store(in: &cancellables)
@@ -79,8 +82,10 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
continue continue
} }
let room = MessageForwardingRoom(id: summary.id, name: summary.name, alias: summary.canonicalAlias, avatar: summary.avatar) rooms.append(.init(id: summary.id,
rooms.append(room) title: summary.name,
description: summary.roomListDescription,
avatar: summary.avatar))
} }
state.rooms = rooms state.rooms = rooms
@@ -68,8 +68,8 @@ private struct MessageForwardingListRow: View {
let context: MessageForwardingScreenViewModel.Context let context: MessageForwardingScreenViewModel.Context
var body: some View { var body: some View {
ListRow(label: .avatar(title: room.name, ListRow(label: .avatar(title: room.title,
description: room.alias ?? room.id, description: room.description,
icon: avatar), icon: avatar),
kind: .selection(isSelected: isSelected) { kind: .selection(isSelected: isSelected) {
context.send(viewAction: .selectRoom(roomID: room.id)) context.send(viewAction: .selectRoom(roomID: room.id))
@@ -0,0 +1,52 @@
//
// Copyright 2022-2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Combine
import SwiftUI
struct RoomSelectionScreenCoordinatorParameters {
let clientProxy: ClientProxyProtocol
let roomSummaryProvider: RoomSummaryProviderProtocol
let mediaProvider: MediaProviderProtocol
}
enum RoomSelectionScreenCoordinatorAction {
case dismiss
case confirm(roomID: String)
}
final class RoomSelectionScreenCoordinator: CoordinatorProtocol {
private var viewModel: RoomSelectionScreenViewModelProtocol
private let actionsSubject: PassthroughSubject<RoomSelectionScreenCoordinatorAction, Never> = .init()
private var cancellables = Set<AnyCancellable>()
var actionsPublisher: AnyPublisher<RoomSelectionScreenCoordinatorAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(parameters: RoomSelectionScreenCoordinatorParameters) {
viewModel = RoomSelectionScreenViewModel(clientProxy: parameters.clientProxy,
roomSummaryProvider: parameters.roomSummaryProvider,
mediaProvider: parameters.mediaProvider)
}
func start() {
viewModel.actionsPublisher.sink { [weak self] action in
switch action {
case .dismiss:
self?.actionsSubject.send(.dismiss)
case .confirm(let roomID):
self?.actionsSubject.send(.confirm(roomID: roomID))
}
}
.store(in: &cancellables)
}
func toPresentable() -> AnyView {
AnyView(RoomSelectionScreen(context: viewModel.context))
}
}
@@ -0,0 +1,39 @@
//
// Copyright 2022-2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Foundation
import MatrixRustSDK
enum RoomSelectionScreenViewModelAction {
case dismiss
case confirm(roomID: String)
}
struct RoomSelectionScreenViewState: BindableState {
var rooms: [RoomSelectionRoom] = []
var selectedRoomID: String?
var bindings = RoomSelectionScreenViewStateBindings()
}
struct RoomSelectionScreenViewStateBindings {
var searchQuery = ""
}
enum RoomSelectionScreenViewAction {
case cancel
case confirm
case selectRoom(roomID: String)
case reachedTop
case reachedBottom
}
struct RoomSelectionRoom: Identifiable, Equatable {
let id: String
let title: String
let description: String
let avatar: RoomAvatar
}
@@ -0,0 +1,105 @@
//
// Copyright 2022-2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Combine
import SwiftUI
typealias RoomSelectionScreenViewModelType = StateStoreViewModel<RoomSelectionScreenViewState, RoomSelectionScreenViewAction>
class RoomSelectionScreenViewModel: RoomSelectionScreenViewModelType, RoomSelectionScreenViewModelProtocol {
private let clientProxy: ClientProxyProtocol
private let roomSummaryProvider: RoomSummaryProviderProtocol
private var actionsSubject: PassthroughSubject<RoomSelectionScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<RoomSelectionScreenViewModelAction, Never> {
actionsSubject.eraseToAnyPublisher()
}
init(clientProxy: ClientProxyProtocol,
roomSummaryProvider: RoomSummaryProviderProtocol,
mediaProvider: MediaProviderProtocol) {
self.clientProxy = clientProxy
self.roomSummaryProvider = roomSummaryProvider
super.init(initialViewState: RoomSelectionScreenViewState(), mediaProvider: mediaProvider)
roomSummaryProvider.roomListPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.updateRooms()
}
.store(in: &cancellables)
context.$viewState
.map(\.bindings.searchQuery)
.removeDuplicates()
.sink { [weak self] searchQuery in
if searchQuery.isEmpty {
self?.roomSummaryProvider.setFilter(.all(filters: []))
} else {
self?.roomSummaryProvider.setFilter(.search(query: searchQuery))
}
}
.store(in: &cancellables)
updateRooms()
}
override func process(viewAction: RoomSelectionScreenViewAction) {
switch viewAction {
case .cancel:
actionsSubject.send(.dismiss)
roomSummaryProvider.setFilter(.all(filters: []))
case .confirm:
guard let selectedRoomID = state.selectedRoomID else {
return
}
actionsSubject.send(.confirm(roomID: selectedRoomID))
case .selectRoom(let roomID):
state.selectedRoomID = roomID
case .reachedTop:
updateVisibleRange(edge: .top)
case .reachedBottom:
updateVisibleRange(edge: .bottom)
}
}
// MARK: - Private
private func updateRooms() {
var rooms = [RoomSelectionRoom]()
for summary in roomSummaryProvider.roomListPublisher.value {
rooms.append(.init(id: summary.id,
title: summary.name,
description: summary.roomListDescription,
avatar: summary.avatar))
}
state.rooms = rooms
}
/// The actual range values don't matter as long as they contain the lower
/// or upper bounds. updateVisibleRange is a hybrid API that powers both
/// sliding sync visible range update and list paginations
/// For lists other than the home screen one we don't care about visible ranges,
/// we just need the respective bounds to be there to trigger a next page load or
/// a reset to just one page
private func updateVisibleRange(edge: UIRectEdge) {
switch edge {
case .top:
roomSummaryProvider.updateVisibleRange(0..<0)
case .bottom:
let roomCount = roomSummaryProvider.roomListPublisher.value.count
roomSummaryProvider.updateVisibleRange(roomCount..<roomCount)
default:
break
}
}
}
@@ -0,0 +1,14 @@
//
// Copyright 2022-2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Combine
@MainActor
protocol RoomSelectionScreenViewModelProtocol {
var actionsPublisher: AnyPublisher<RoomSelectionScreenViewModelAction, Never> { get }
var context: RoomSelectionScreenViewModelType.Context { get }
}
@@ -0,0 +1,104 @@
//
// Copyright 2022-2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Compound
import SwiftUI
struct RoomSelectionScreen: View {
@ObservedObject var context: RoomSelectionScreenViewModel.Context
var body: some View {
Form {
Section {
ForEach(context.viewState.rooms) { room in
RoomSelectionListRow(room: room,
isSelected: context.viewState.selectedRoomID == room.id,
context: context)
}
// Replace these with ScrollView's `scrollPosition` when dropping iOS 16.
} header: {
emptyRectangle
.onAppear {
context.send(viewAction: .reachedTop)
}
} footer: {
emptyRectangle
.onAppear {
context.send(viewAction: .reachedBottom)
}
}
}
.compoundList()
.navigationTitle(L10n.screenRoomlistMainSpaceTitle)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(L10n.actionCancel) {
context.send(viewAction: .cancel)
}
}
ToolbarItem(placement: .confirmationAction) {
Button(L10n.actionShare) {
context.send(viewAction: .confirm)
}
.disabled(context.viewState.selectedRoomID == nil)
}
}
.searchController(query: $context.searchQuery, showsCancelButton: false)
.compoundSearchField()
.disableAutocorrection(true)
}
/// The greedy size of Rectangle can create an issue with the navigation bar when the search is highlighted, so is best to use a fixed frame instead of hidden() or EmptyView()
private var emptyRectangle: some View {
Rectangle()
.frame(width: 0, height: 0)
}
}
private struct RoomSelectionListRow: View {
@Environment(\.dynamicTypeSize) var dynamicTypeSize
let room: RoomSelectionRoom
let isSelected: Bool
let context: RoomSelectionScreenViewModel.Context
var body: some View {
ListRow(label: .avatar(title: room.title,
description: room.description,
icon: avatar),
kind: .selection(isSelected: isSelected) {
context.send(viewAction: .selectRoom(roomID: room.id))
})
}
@ViewBuilder @MainActor
var avatar: some View {
if dynamicTypeSize < .accessibility3 {
RoomAvatarImage(avatar: room.avatar,
avatarSize: .room(on: .roomSelection),
mediaProvider: context.mediaProvider)
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
.accessibilityHidden(true)
}
}
}
// MARK: - Previews
struct RoomSelectionScreen_Previews: PreviewProvider, TestablePreview {
static var previews: some View {
let summaryProvider = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
let viewModel = RoomSelectionScreenViewModel(clientProxy: ClientProxyMock(.init()),
roomSummaryProvider: summaryProvider,
mediaProvider: MediaProviderMock(configuration: .init()))
NavigationStack {
RoomSelectionScreen(context: viewModel.context)
}
}
}
@@ -833,7 +833,7 @@ class ClientProxy: ClientProxyProtocol {
alternateRoomSummaryProvider = RoomSummaryProvider(roomListService: roomListService, alternateRoomSummaryProvider = RoomSummaryProvider(roomListService: roomListService,
eventStringBuilder: eventStringBuilder, eventStringBuilder: eventStringBuilder,
name: "MessageForwarding", name: "AlternateAllRooms",
notificationSettings: notificationSettings, notificationSettings: notificationSettings,
appSettings: appSettings) appSettings: appSettings)
try await alternateRoomSummaryProvider?.setRoomList(roomListService.allRooms()) try await alternateRoomSummaryProvider?.setRoomList(roomListService.allRooms())
@@ -69,6 +69,20 @@ extension RoomSummary: CustomStringConvertible {
- notificationMode: \(notificationMode?.rawValue ?? "nil") - notificationMode: \(notificationMode?.rawValue ?? "nil")
""" """
} }
/// Used where summaries are shown in a list e.g. message forwarding,
/// global search, share destination list etc.
var roomListDescription: String {
if isDirect {
return canonicalAlias ?? ""
}
if let alias = canonicalAlias {
return alias
}
return heroes.compactMap(\.displayName).formatted(.list(type: .and))
}
} }
extension RoomSummary { extension RoomSummary {
@@ -7,6 +7,7 @@
import Combine import Combine
import Foundation import Foundation
import IntentsUI
import MatrixRustSDK import MatrixRustSDK
import UIKit import UIKit
@@ -14,7 +15,9 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
private let roomProxy: JoinedRoomProxyProtocol private let roomProxy: JoinedRoomProxyProtocol
private let liveTimelineProvider: RoomTimelineProviderProtocol private let liveTimelineProvider: RoomTimelineProviderProtocol
private let timelineItemFactory: RoomTimelineItemFactoryProtocol private let timelineItemFactory: RoomTimelineItemFactoryProtocol
private let mediaProvider: MediaProviderProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let serialDispatchQueue: DispatchQueue private let serialDispatchQueue: DispatchQueue
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>() let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
@@ -40,11 +43,14 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
timelineProxy: TimelineProxyProtocol, timelineProxy: TimelineProxyProtocol,
initialFocussedEventID: String?, initialFocussedEventID: String?,
timelineItemFactory: RoomTimelineItemFactoryProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol,
mediaProvider: MediaProviderProtocol,
appSettings: AppSettings) { appSettings: AppSettings) {
self.roomProxy = roomProxy self.roomProxy = roomProxy
liveTimelineProvider = timelineProxy.timelineProvider liveTimelineProvider = timelineProxy.timelineProvider
self.timelineItemFactory = timelineItemFactory self.timelineItemFactory = timelineItemFactory
self.mediaProvider = mediaProvider
self.appSettings = appSettings self.appSettings = appSettings
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility) serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
activeTimeline = timelineProxy activeTimeline = timelineProxy
@@ -153,11 +159,63 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
intentionalMentions: intentionalMentions) { intentionalMentions: intentionalMentions) {
case .success: case .success:
MXLog.info("Finished sending message") MXLog.info("Finished sending message")
await donateSendMessageIntent()
case .failure(let error): case .failure(let error):
MXLog.error("Failed sending message with error: \(error)") MXLog.error("Failed sending message with error: \(error)")
} }
} }
private func donateSendMessageIntent() async {
guard let displayName = roomProxy.details.name ?? roomProxy.details.canonicalAlias, !displayName.isEmpty else {
MXLog.error("Failed donating send message intent, room missing name or alias.")
return
}
let groupName = INSpeakableString(spokenPhrase: displayName)
let sendMessageIntent = INSendMessageIntent(recipients: nil,
outgoingMessageType: .outgoingMessageText,
content: nil,
speakableGroupName: groupName,
conversationIdentifier: roomProxy.id,
serviceName: nil,
sender: nil,
attachments: nil)
let avatarURL = switch roomProxy.details.avatar {
case .room(_, _, let avatarURL):
avatarURL
case .heroes(let userProfiles):
userProfiles.first?.avatarURL
}
func addPlacehoder() {
if let imageData = Avatars.generatePlaceholderAvatarImageData(name: displayName, id: roomProxy.id, size: .init(width: 100, height: 100)) {
sendMessageIntent.setImage(INImage(imageData: imageData), forParameterNamed: \.speakableGroupName)
}
}
if let avatarURL {
let mediaSource = MediaSourceProxy(url: avatarURL, mimeType: nil)
if case let .success(avatarData) = await mediaProvider.loadThumbnailForSource(source: mediaSource, size: .init(width: 100, height: 100)) {
sendMessageIntent.setImage(INImage(imageData: avatarData), forParameterNamed: \.speakableGroupName)
} else {
addPlacehoder()
}
} else {
addPlacehoder()
}
let interaction = INInteraction(intent: sendMessageIntent, response: nil)
do {
try await interaction.donate()
} catch {
MXLog.error("Failed donating send message intent with error: \(error)")
}
}
func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async { func toggleReaction(_ reaction: String, to eventOrTransactionID: EventOrTransactionId) async {
MXLog.info("Toggle reaction \(reaction) to \(eventOrTransactionID)") MXLog.info("Toggle reaction \(reaction) to \(eventOrTransactionID)")
@@ -10,16 +10,19 @@ import Foundation
struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol { struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
initialFocussedEventID: String?, initialFocussedEventID: String?,
timelineItemFactory: RoomTimelineItemFactoryProtocol) -> RoomTimelineControllerProtocol { timelineItemFactory: RoomTimelineItemFactoryProtocol,
mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol {
RoomTimelineController(roomProxy: roomProxy, RoomTimelineController(roomProxy: roomProxy,
timelineProxy: roomProxy.timeline, timelineProxy: roomProxy.timeline,
initialFocussedEventID: initialFocussedEventID, initialFocussedEventID: initialFocussedEventID,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: mediaProvider,
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
} }
func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol, func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol,
timelineItemFactory: RoomTimelineItemFactoryProtocol) async -> RoomTimelineControllerProtocol? { timelineItemFactory: RoomTimelineItemFactoryProtocol,
mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? {
guard let pinnedEventsTimeline = await roomProxy.pinnedEventsTimeline else { guard let pinnedEventsTimeline = await roomProxy.pinnedEventsTimeline else {
return nil return nil
} }
@@ -27,6 +30,7 @@ struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
timelineProxy: pinnedEventsTimeline, timelineProxy: pinnedEventsTimeline,
initialFocussedEventID: nil, initialFocussedEventID: nil,
timelineItemFactory: timelineItemFactory, timelineItemFactory: timelineItemFactory,
mediaProvider: mediaProvider,
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
} }
} }
@@ -11,9 +11,11 @@ import Foundation
protocol RoomTimelineControllerFactoryProtocol { protocol RoomTimelineControllerFactoryProtocol {
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
initialFocussedEventID: String?, initialFocussedEventID: String?,
timelineItemFactory: RoomTimelineItemFactoryProtocol) -> RoomTimelineControllerProtocol timelineItemFactory: RoomTimelineItemFactoryProtocol,
mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol
func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol, func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol,
timelineItemFactory: RoomTimelineItemFactoryProtocol) async -> RoomTimelineControllerProtocol? timelineItemFactory: RoomTimelineItemFactoryProtocol,
mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol?
} }
// sourcery: AutoMockable // sourcery: AutoMockable
@@ -72,19 +72,19 @@ extension SessionDirectories {
init() { init() {
let sessionDirectoryName = UUID().uuidString let sessionDirectoryName = UUID().uuidString
dataDirectory = .sessionsBaseDirectory.appending(component: sessionDirectoryName) dataDirectory = .sessionsBaseDirectory.appending(component: sessionDirectoryName)
cacheDirectory = .cachesBaseDirectory.appending(component: sessionDirectoryName) cacheDirectory = .sessionCachesBaseDirectory.appending(component: sessionDirectoryName)
} }
/// Creates the session directories for a user who signed in before the data directory was stored. /// Creates the session directories for a user who signed in before the data directory was stored.
init(userID: String) { init(userID: String) {
dataDirectory = .legacySessionDirectory(for: userID) dataDirectory = .legacySessionDirectory(for: userID)
cacheDirectory = .cachesBaseDirectory.appending(component: dataDirectory.lastPathComponent) cacheDirectory = .sessionCachesBaseDirectory.appending(component: dataDirectory.lastPathComponent)
} }
/// Creates the session directories for a user who has a single session directory stored without a separate caches directory. /// Creates the session directories for a user who has a single session directory stored without a separate caches directory.
init(dataDirectory: URL) { init(dataDirectory: URL) {
self.dataDirectory = dataDirectory self.dataDirectory = dataDirectory
cacheDirectory = .cachesBaseDirectory.appending(component: dataDirectory.lastPathComponent) cacheDirectory = .sessionCachesBaseDirectory.appending(component: dataDirectory.lastPathComponent)
} }
} }
@@ -0,0 +1,21 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Foundation
enum ShareExtensionConstants {
static let urlPath = "share"
}
enum ShareExtensionPayload: Hashable, Codable {
case mediaFile(roomID: String?, mediaFile: ShareExtensionMediaFile)
}
struct ShareExtensionMediaFile: Hashable, Codable {
let url: URL
let suggestedName: String?
}
@@ -684,6 +684,7 @@ class MockScreen: Identifiable {
timelineItemFactory: RoomTimelineItemFactory(userID: "@alice:matrix.org", timelineItemFactory: RoomTimelineItemFactory(userID: "@alice:matrix.org",
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()), attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
stateEventStringBuilder: RoomStateEventStringBuilder(userID: "@alice:matrix.org")), stateEventStringBuilder: RoomStateEventStringBuilder(userID: "@alice:matrix.org")),
mediaProvider: MediaProviderMock(configuration: .init()),
appSettings: ServiceLocator.shared.settings) appSettings: ServiceLocator.shared.settings)
let flowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)), let flowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
+1 -1
View File
@@ -54,7 +54,7 @@
<string>Application</string> <string>Application</string>
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>io.element</string> <string>$(BASE_BUNDLE_IDENTIFIER)</string>
</array> </array>
</dict> </dict>
</array> </array>
+2 -1
View File
@@ -66,7 +66,7 @@ targets:
CFBundleTypeRole: Editor, CFBundleTypeRole: Editor,
CFBundleURLName: "Application", CFBundleURLName: "Application",
CFBundleURLSchemes: [ CFBundleURLSchemes: [
io.element $(BASE_BUNDLE_IDENTIFIER)
] ]
} }
] ]
@@ -189,6 +189,7 @@ targets:
dependencies: dependencies:
- target: NSE - target: NSE
- target: ShareExtension
# not used yet # not used yet
# - target: NCE # - target: NCE
- package: MatrixRustSDK - package: MatrixRustSDK
@@ -66,10 +66,10 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
handler = contentHandler handler = contentHandler
modifiedContent = request.content.mutableCopy() as? UNMutableNotificationContent modifiedContent = request.content.mutableCopy() as? UNMutableNotificationContent
NSELogger.configure(logLevel: settings.logLevel) ExtensionLogger.configure(currentTarget: "nse", logLevel: settings.logLevel)
MXLog.info("\(tag) #########################################") MXLog.info("\(tag) #########################################")
NSELogger.logMemory(with: tag) ExtensionLogger.logMemory(with: tag)
MXLog.info("\(tag) Payload came: \(request.content.userInfo)") MXLog.info("\(tag) Payload came: \(request.content.userInfo)")
Self.serialQueue.sync { Self.serialQueue.sync {
@@ -201,7 +201,7 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
deinit { deinit {
cleanUp() cleanUp()
NSELogger.logMemory(with: tag) ExtensionLogger.logMemory(with: tag)
MXLog.info("\(tag) deinit") MXLog.info("\(tag) deinit")
} }
+1 -1
View File
@@ -77,7 +77,7 @@ targets:
- path: ../../ElementX/Sources/Application/AppSettings.swift - path: ../../ElementX/Sources/Application/AppSettings.swift
- path: ../../ElementX/Sources/Generated/Assets.swift - path: ../../ElementX/Sources/Generated/Assets.swift
- path: ../../ElementX/Sources/Generated/Strings.swift - path: ../../ElementX/Sources/Generated/Strings.swift
- path: ../../ElementX/Sources/Other/AvatarSize.swift - path: ../../ElementX/Sources/Other/Avatars.swift
- path: ../../ElementX/Sources/Other/CurrentValuePublisher.swift - path: ../../ElementX/Sources/Other/CurrentValuePublisher.swift
- path: ../../ElementX/Sources/Other/Extensions/AttributedString.swift - path: ../../ElementX/Sources/Other/Extensions/AttributedString.swift
- path: ../../ElementX/Sources/Other/Extensions/Bundle.swift - path: ../../ElementX/Sources/Other/Extensions/Bundle.swift
@@ -701,6 +701,12 @@ extension PreviewTests {
} }
} }
func test_roomSelectionScreen() {
for preview in RoomSelectionScreen_Previews._allPreviews {
assertSnapshots(matching: preview)
}
}
func test_sFNumberedListView() { func test_sFNumberedListView() {
for preview in SFNumberedListView_Previews._allPreviews { for preview in SFNumberedListView_Previews._allPreviews {
assertSnapshots(matching: preview) assertSnapshots(matching: preview)
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:481969f82410ed28b3c259d53e88310fd5669d1c6041020c99c47a7a64fde666 oid sha256:ec8339fcf606ac520989a6915da4f3a15a43d049e49c3e93524ad70bd8097778
size 151180 size 148064
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ac9dc777f9bdaca948c7a5cf56aebb163ff80be491a124c78084b5d1309396af oid sha256:aa3fb400d0af05b6c81181a040f3746c11fafe82703f7587ae9d98cdcce44386
size 151589 size 148467
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:656ab8534b3b28a11a09b7ee3bd735de4f9173687006d8346093ec8fa70caf54 oid sha256:ed42c3e99db34f3dedb33b5faaf768caee7d15d8d5dfb50fbed0f97da2b990f0
size 88511 size 85859
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:6dece45267d9514a73241f045de87eb76d7d87ccc236aed3d114ccfbe9507d74 oid sha256:f33f9d8cc790b5a03c0da8423e948bc9faffa7580e587d509e13fb691a255eaf
size 88837 size 86196
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:7001c732a5f38e3b8093488f80504a474c0b8f4f494e68793029f387047522e8 oid sha256:1705df7384f16656d63d4efca503b4533cd3d67f54733855f284ddc49cdf973f
size 152484 size 149086
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:4d213faa018f71016ebc6f31691ac3cbf97849e8a739937d9e351b92bef7877c oid sha256:de6fe930505a8b4e9ab2b3052ebd1345118eaa64149381f28b3142447722d61a
size 154392 size 151009
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:ee5ecf468c941282295014187f8fc03929c122f343465113496e20a8cca738d6 oid sha256:96596029a45f8bc48da6f8cbd77f09eb65236ac111534d9dec7d45e0e083cf32
size 101988 size 99144
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:53268d051594a0dd4250a79b5b3093a4f57f4104250c990e6bb276c751093b94 oid sha256:dd2bd5da5c119e827b8b195971a34b645d5e47f577bf622e5856412c91d616e2
size 101810 size 98976
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:75e855ee7f0e97644797ed467953cd618fbc64a1ca185e6784cd24aa5cdef613
size 146330
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7749e68e119cf015e5110e889ef29f283af3b7f4f71840bba60b7abdfbea787b
size 147778
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8f53f2f4cc267e92bab7d8bf4a1fdfd307609312cf7e623ae43a6a254971c1f6
size 97028
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3e76ee2d1d8b3dee92997af404d846a889e9ccea0862b848c8d2386d4489220b
size 98291
@@ -0,0 +1,141 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import IntentsUI
import SwiftUI
class ShareExtensionViewController: UIViewController {
private let appSettings: CommonSettingsProtocol = AppSettings()
private let hostingController = UIHostingController(rootView: ShareExtensionView())
override func viewDidLoad() {
super.viewDidLoad()
addChild(hostingController)
view.addMatchedSubview(hostingController.view)
hostingController.didMove(toParent: self)
MXLog.configure(currentTarget: "shareextension", filePrefix: "shareextension", logLevel: appSettings.logLevel)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Task {
if let payload = await prepareSharePayload() {
await self.openMainApp(payload: payload)
}
self.dismiss()
}
}
// MARK: - Private
private func prepareSharePayload() async -> ShareExtensionPayload? {
guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem,
let itemProvider = extensionItem.attachments?.first else {
return nil
}
guard let contentType = itemProvider.preferredContentType,
let preferredExtension = contentType.preferredFilenameExtension else {
MXLog.error("Invalid NSItemProvider: \(itemProvider)")
return nil
}
let roomID = (extensionContext?.intent as? INSendMessageIntent)?.conversationIdentifier
let providerSuggestedName = itemProvider.suggestedName
let providerDescription = itemProvider.description
let shareData: Data? = await withCheckedContinuation { continuation in
_ = itemProvider.loadDataRepresentation(for: contentType) { data, error in
if let error {
MXLog.error("Failed processing NSItemProvider: \(providerDescription) with error: \(error)")
continuation.resume(returning: nil)
return
}
guard let data else {
MXLog.error("Invalid NSItemProvider data: \(providerDescription)")
continuation.resume(returning: nil)
return
}
continuation.resume(returning: data)
}
}
guard let shareData else {
return nil
}
do {
let url: URL
if let filename = providerSuggestedName {
let hasExtension = !(filename as NSString).pathExtension.isEmpty
let filename = hasExtension ? filename : "\(filename).\(preferredExtension)"
url = try FileManager.default.writeDataToTemporaryDirectory(data: shareData, fileName: filename)
} else {
let filename = "\(UUID().uuidString).\(preferredExtension)"
url = try FileManager.default.writeDataToTemporaryDirectory(data: shareData, fileName: filename)
}
return .mediaFile(roomID: roomID, mediaFile: .init(url: url, suggestedName: providerSuggestedName))
} catch {
MXLog.error("Failed storing NSItemProvider data \(providerDescription) with error: \(error)")
return nil
}
}
private func openMainApp(payload: ShareExtensionPayload) async {
guard let payload = urlEncodeSharePayload(payload) else {
MXLog.error("Failed preparing share payload")
return
}
guard let url = URL(string: "\(InfoPlistReader.main.baseBundleIdentifier):/\(ShareExtensionConstants.urlPath)?\(payload)") else {
MXLog.error("Failed retrieving main application scheme")
return
}
await openURL(url)
}
private func urlEncodeSharePayload(_ payload: ShareExtensionPayload) -> String? {
let data: Data
do {
data = try JSONEncoder().encode(payload)
} catch {
MXLog.error("Failed encoding share payload with error: \(error)")
return nil
}
guard let jsonString = String(data: data, encoding: .utf8) else {
MXLog.error("Invalid payload data")
return nil
}
return jsonString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
}
private func dismiss() {
extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
}
private func openURL(_ url: URL) async {
var responder: UIResponder? = self
while responder != nil {
if let application = responder as? UIApplication {
await application.open(url)
return
}
responder = responder?.next
}
}
}
@@ -0,0 +1,23 @@
//
// Copyright 2024 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only
// Please see LICENSE in the repository root for full details.
//
import Compound
import SwiftUI
struct ShareExtensionView: View {
var body: some View {
ZStack {
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.background(.compound.bgCanvasDefault)
}
}
#Preview {
ShareExtensionView()
}
+55
View File
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>$(PRODUCT_DISPLAY_NAME)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>IntentsSupported</key>
<array>
<string>INSendMessageIntent</string>
</array>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareExtensionViewController</string>
</dict>
<key>appGroupIdentifier</key>
<string>$(APP_GROUP_IDENTIFIER)</string>
<key>baseBundleIdentifier</key>
<string>$(BASE_BUNDLE_IDENTIFIER)</string>
<key>keychainAccessGroupIdentifier</key>
<string>$(KEYCHAIN_ACCESS_GROUP_IDENTIFIER)</string>
<key>productionAppName</key>
<string>$(PRODUCTION_APP_NAME)</string>
</dict>
</plist>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.io.element</string>
</array>
</dict>
</plist>
+85
View File
@@ -0,0 +1,85 @@
name: ShareExtension
schemes:
ShareExtension:
analyze:
config: Debug
archive:
config: Release
build:
targets:
ShareExtension:
- running
- testing
- profiling
- analyzing
- archiving
profile:
config: Release
run:
askForAppToLaunch: true
config: Debug
debugEnabled: false
disableMainThreadChecker: false
launchAutomaticallySubstyle: 2
test:
config: Debug
disableMainThreadChecker: false
targets:
ShareExtension:
type: app-extension
platform: iOS
dependencies:
- package: MatrixRustSDK
- package: Collections
- package: Compound
info:
path: ../SupportingFiles/Info.plist
properties:
CFBundleDisplayName: $(PRODUCT_DISPLAY_NAME)
CFBundleShortVersionString: $(MARKETING_VERSION)
CFBundleVersion: $(CURRENT_PROJECT_VERSION)
appGroupIdentifier: $(APP_GROUP_IDENTIFIER)
baseBundleIdentifier: $(BASE_BUNDLE_IDENTIFIER)
keychainAccessGroupIdentifier: $(KEYCHAIN_ACCESS_GROUP_IDENTIFIER)
productionAppName: $(PRODUCTION_APP_NAME)
NSExtension:
NSExtensionPointIdentifier: com.apple.share-services
NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).ShareExtensionViewController
NSExtensionAttributes:
IntentsSupported: [
INSendMessageIntent,
]
NSExtensionActivationRule:
NSExtensionActivationSupportsFileWithMaxCount: 1
NSExtensionActivationSupportsImageWithMaxCount: 1
NSExtensionActivationSupportsMovieWithMaxCount: 1
settings:
base:
PRODUCT_NAME: ShareExtension
PRODUCT_DISPLAY_NAME: $(APP_DISPLAY_NAME)
PRODUCT_BUNDLE_IDENTIFIER: ${BASE_BUNDLE_IDENTIFIER}.shareextension
MARKETING_VERSION: $(MARKETING_VERSION)
CURRENT_PROJECT_VERSION: $(CURRENT_PROJECT_VERSION)
DEVELOPMENT_TEAM: $(DEVELOPMENT_TEAM)
CODE_SIGN_ENTITLEMENTS: ShareExtension/SupportingFiles/ShareExtension.entitlements
sources:
- path: ../Sources
- path: ../SupportingFiles
- path: ../../ElementX/Sources/ShareExtension
- path: ../../ElementX/Sources/Application/AppSettings.swift
- path: ../../ElementX/Sources/Other/Extensions/Bundle.swift
- path: ../../ElementX/Sources/Other/Extensions/FileManager.swift
- path: ../../ElementX/Sources/Other/Extensions/NSItemProvider.swift
- path: ../../ElementX/Sources/Other/Extensions/ProcessInfo.swift
- path: ../../ElementX/Sources/Other/Extensions/UIView.swift
- path: ../../ElementX/Sources/Other/Extensions/URL.swift
- path: ../../ElementX/Sources/Other/InfoPlistReader.swift
- path: ../../ElementX/Sources/Other/Logging
- path: ../../ElementX/Sources/Other/UserPreference.swift
- path: ../../ElementX/Sources/UITests/UITestsScreenIdentifier.swift
@@ -73,12 +73,12 @@ final class MediaProviderTests: XCTestCase {
} }
func test_whenImageFromSourceWithSourceNil_nilReturned() throws { func test_whenImageFromSourceWithSourceNil_nilReturned() throws {
let image = mediaProvider.imageFromSource(nil, size: AvatarSize.room(on: .timeline).scaledSize) let image = mediaProvider.imageFromSource(nil, size: Avatars.Size.room(on: .timeline).scaledSize)
XCTAssertNil(image) XCTAssertNil(image)
} }
func test_whenImageFromSourceWithSourceNotNilAndImageCacheContainsImage_ImageIsReturned() throws { func test_whenImageFromSourceWithSourceNotNilAndImageCacheContainsImage_ImageIsReturned() throws {
let avatarSize = AvatarSize.room(on: .timeline) let avatarSize = Avatars.Size.room(on: .timeline)
let url = URL.picturesDirectory let url = URL.picturesDirectory
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}" let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let imageForKey = UIImage() let imageForKey = UIImage()
@@ -90,12 +90,12 @@ final class MediaProviderTests: XCTestCase {
func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws { func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws {
let image = mediaProvider.imageFromSource(MediaSourceProxy(url: URL.picturesDirectory, mimeType: "image/jpeg"), let image = mediaProvider.imageFromSource(MediaSourceProxy(url: URL.picturesDirectory, mimeType: "image/jpeg"),
size: AvatarSize.room(on: .timeline).scaledSize) size: Avatars.Size.room(on: .timeline).scaledSize)
XCTAssertNil(image) XCTAssertNil(image)
} }
func test_whenLoadImageFromSourceAndImageCacheContainsImage_successIsReturned() async throws { func test_whenLoadImageFromSourceAndImageCacheContainsImage_successIsReturned() async throws {
let avatarSize = AvatarSize.room(on: .timeline) let avatarSize = Avatars.Size.room(on: .timeline)
let url = URL.picturesDirectory let url = URL.picturesDirectory
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}" let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let imageForKey = UIImage() let imageForKey = UIImage()
@@ -106,7 +106,7 @@ final class MediaProviderTests: XCTestCase {
} }
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageSucceeds_successIsReturned() async throws { func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageSucceeds_successIsReturned() async throws {
let avatarSize = AvatarSize.room(on: .timeline) let avatarSize = Avatars.Size.room(on: .timeline)
let url = URL.picturesDirectory let url = URL.picturesDirectory
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}" let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let imageForKey = UIImage() let imageForKey = UIImage()
@@ -117,7 +117,7 @@ final class MediaProviderTests: XCTestCase {
} }
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageThumbnailIsLoaded() async throws { func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageThumbnailIsLoaded() async throws {
let avatarSize = AvatarSize.room(on: .timeline) let avatarSize = Avatars.Size.room(on: .timeline)
let expectedImage = try loadTestImage() let expectedImage = try loadTestImage()
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData() mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData()
@@ -133,7 +133,7 @@ final class MediaProviderTests: XCTestCase {
} }
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageIsStored() async throws { func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageIsStored() async throws {
let avatarSize = AvatarSize.room(on: .timeline) let avatarSize = Avatars.Size.room(on: .timeline)
let url = URL.picturesDirectory let url = URL.picturesDirectory
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}" let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
let expectedImage = try loadTestImage() let expectedImage = try loadTestImage()
@@ -165,7 +165,7 @@ final class MediaProviderTests: XCTestCase {
mediaLoader.loadMediaThumbnailForSourceWidthHeightThrowableError = MediaProviderTestsError.error mediaLoader.loadMediaThumbnailForSourceWidthHeightThrowableError = MediaProviderTestsError.error
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory, mimeType: "image/jpeg"), let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory, mimeType: "image/jpeg"),
size: AvatarSize.room(on: .timeline).scaledSize) size: Avatars.Size.room(on: .timeline).scaledSize)
switch result { switch result {
case .success: case .success:
XCTFail("Should fail") XCTFail("Should fail")
@@ -191,7 +191,7 @@ final class MediaProviderTests: XCTestCase {
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = Data() mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = Data()
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory, mimeType: "image/jpeg"), let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory, mimeType: "image/jpeg"),
size: AvatarSize.room(on: .timeline).scaledSize) size: Avatars.Size.room(on: .timeline).scaledSize)
switch result { switch result {
case .success: case .success:
XCTFail("Should fail") XCTFail("Should fail")
@@ -31,7 +31,7 @@ class RestorationTokenTests: XCTestCase {
XCTAssertNil(decodedToken.pusherNotificationClientIdentifier, "There should not be a push notification client ID.") XCTAssertNil(decodedToken.pusherNotificationClientIdentifier, "There should not be a push notification client ID.")
XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, .sessionsBaseDirectory.appending(component: "@user_example.com"), XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, .sessionsBaseDirectory.appending(component: "@user_example.com"),
"The session directory should match the original location set by the Rust SDK from our base directory.") "The session directory should match the original location set by the Rust SDK from our base directory.")
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .cachesBaseDirectory.appending(component: "@user_example.com"), XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: "@user_example.com"),
"The cache directory should be derived from the session directory but in the caches directory.") "The cache directory should be derived from the session directory but in the caches directory.")
} }
@@ -60,7 +60,7 @@ class RestorationTokenTests: XCTestCase {
"The push notification client identifier should not be changed.") "The push notification client identifier should not be changed.")
XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, originalToken.sessionDirectory, XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, originalToken.sessionDirectory,
"The session directory should not be changed.") "The session directory should not be changed.")
XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .cachesBaseDirectory.appending(component: sessionDirectoryName), XCTAssertEqual(decodedToken.sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: sessionDirectoryName),
"The cache directory should be derived from the session directory but in the caches directory.") "The cache directory should be derived from the session directory but in the caches directory.")
} }
@@ -75,7 +75,7 @@ class RestorationTokenTests: XCTestCase {
oidcData: "data-from-mas", oidcData: "data-from-mas",
slidingSyncVersion: .native), slidingSyncVersion: .native),
sessionDirectory: .sessionsBaseDirectory.appending(component: sessionDirectoryName), sessionDirectory: .sessionsBaseDirectory.appending(component: sessionDirectoryName),
cacheDirectory: .cachesBaseDirectory.appending(component: sessionDirectoryName), cacheDirectory: .sessionCachesBaseDirectory.appending(component: sessionDirectoryName),
passphrase: "passphrase", passphrase: "passphrase",
pusherNotificationClientIdentifier: "pusher-identifier") pusherNotificationClientIdentifier: "pusher-identifier")
let data = try JSONEncoder().encode(originalToken) let data = try JSONEncoder().encode(originalToken)
@@ -218,6 +218,31 @@ class RoomFlowCoordinatorTests: XCTestCase {
XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator) XCTAssert(navigationStackCoordinator.stackCoordinators.first is RoomScreenCoordinator)
} }
func testShareRoute() async throws {
await setupRoomFlowCoordinator()
try await process(route: .room(roomID: "1", via: []))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
let sharePayload: ShareExtensionPayload = .mediaFile(roomID: "1", mediaFile: .init(url: .picturesDirectory, suggestedName: nil))
try await process(route: .share(sharePayload))
XCTAssert(navigationStackCoordinator.rootCoordinator is RoomScreenCoordinator)
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
XCTAssertTrue((navigationStackCoordinator.sheetCoordinator as? NavigationStackCoordinator)?.rootCoordinator is MediaUploadPreviewScreenCoordinator)
try await process(route: .childRoom(roomID: "2", via: []))
XCTAssertNil(navigationStackCoordinator.sheetCoordinator)
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 1)
try await process(route: .share(sharePayload))
XCTAssertEqual(navigationStackCoordinator.stackCoordinators.count, 0)
XCTAssertTrue((navigationStackCoordinator.sheetCoordinator as? NavigationStackCoordinator)?.rootCoordinator is MediaUploadPreviewScreenCoordinator)
}
// MARK: - Private // MARK: - Private
private func process(route: AppRoute) async throws { private func process(route: AppRoute) async throws {
@@ -21,7 +21,7 @@ class SessionDirectoriesTests: XCTestCase {
// Then the directories should be generated in the correct location, using an escaped version of the user ID // Then the directories should be generated in the correct location, using an escaped version of the user ID
XCTAssertEqual(sessionDirectories.dataDirectory, .sessionsBaseDirectory.appending(component: "@user_matrix.org")) XCTAssertEqual(sessionDirectories.dataDirectory, .sessionsBaseDirectory.appending(component: "@user_matrix.org"))
XCTAssertEqual(sessionDirectories.cacheDirectory, .cachesBaseDirectory.appending(component: "@user_matrix.org")) XCTAssertEqual(sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: "@user_matrix.org"))
} }
func testInitWithDataDirectory() { func testInitWithDataDirectory() {
@@ -34,7 +34,7 @@ class SessionDirectoriesTests: XCTestCase {
// Then the data directory should remain unchanged and the caches directory should be generated. // Then the data directory should remain unchanged and the caches directory should be generated.
XCTAssertEqual(sessionDirectories.dataDirectory, sessionDirectory) XCTAssertEqual(sessionDirectories.dataDirectory, sessionDirectory)
XCTAssertEqual(sessionDirectories.cacheDirectory, .cachesBaseDirectory.appending(component: sessionDirectoryName)) XCTAssertEqual(sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: sessionDirectoryName))
} }
func testPathOutput() { func testPathOutput() {
@@ -211,8 +211,8 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator) XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0) XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
XCTAssertNotNil(detailCoordinator) XCTAssertNotNil(detailCoordinator)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 1) XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 1)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "1") XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "1")
// A child event route should push a new room screen onto the stack and focus on the event. // A child event route should push a new room screen onto the stack and focus on the event.
userSessionFlowCoordinator.handleAppRoute(.childEvent(eventID: "2", roomID: "2", via: []), animated: true) userSessionFlowCoordinator.handleAppRoute(.childEvent(eventID: "2", roomID: "2", via: []), animated: true)
@@ -221,27 +221,50 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 1) XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 1)
XCTAssertTrue(detailNavigationStack?.stackCoordinators.first is RoomScreenCoordinator) XCTAssertTrue(detailNavigationStack?.stackCoordinators.first is RoomScreenCoordinator)
XCTAssertNotNil(detailCoordinator) XCTAssertNotNil(detailCoordinator)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 2) XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 2)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "2") XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "2")
// A subsequent regular event route should clear the stack and set the new room as the root of the stack. // A subsequent regular event route should clear the stack and set the new room as the root of the stack.
try await process(route: .event(eventID: "3", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3")) try await process(route: .event(eventID: "3", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3"))
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator) XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0) XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
XCTAssertNotNil(detailCoordinator) XCTAssertNotNil(detailCoordinator)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 3) XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 3)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "3") XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "3")
// A regular event route for the same room should set a new instance of the room as the root of the stack. // A regular event route for the same room should set a new instance of the room as the root of the stack.
try await process(route: .event(eventID: "4", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3")) try await process(route: .event(eventID: "4", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3"))
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator) XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0) XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
XCTAssertNotNil(detailCoordinator) XCTAssertNotNil(detailCoordinator)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 4) XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 4)
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "4", XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "4",
"A new timeline should be created for the same room ID, so that the screen isn't stale while loading.") "A new timeline should be created for the same room ID, so that the screen isn't stale while loading.")
} }
func testShareRouteWithoutRoom() async throws {
try await process(route: .settings, expectedState: .settingsScreen(selectedRoomID: nil))
XCTAssertTrue((splitCoordinator?.sheetCoordinator as? NavigationStackCoordinator)?.rootCoordinator is SettingsScreenCoordinator)
let sharePayload: ShareExtensionPayload = .mediaFile(roomID: nil, mediaFile: .init(url: .picturesDirectory, suggestedName: nil))
try await process(route: .share(sharePayload),
expectedState: .shareExtensionRoomList(sharePayload: sharePayload))
XCTAssertTrue((splitCoordinator?.sheetCoordinator as? NavigationStackCoordinator)?.rootCoordinator is RoomSelectionScreenCoordinator)
}
func testShareRouteWithRoom() async throws {
try await process(route: .event(eventID: "1", roomID: "1", via: []), expectedState: .roomList(selectedRoomID: "1"))
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
let sharePayload: ShareExtensionPayload = .mediaFile(roomID: "2", mediaFile: .init(url: .picturesDirectory, suggestedName: nil))
try await process(route: .share(sharePayload),
expectedState: .roomList(selectedRoomID: "2"))
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
XCTAssertTrue((splitCoordinator?.sheetCoordinator as? NavigationStackCoordinator)?.rootCoordinator is MediaUploadPreviewScreenCoordinator)
}
// MARK: - Private // MARK: - Private
private func process(route: AppRoute, expectedState: UserSessionFlowCoordinatorStateMachine.State) async throws { private func process(route: AppRoute, expectedState: UserSessionFlowCoordinatorStateMachine.State) async throws {
+1
View File
@@ -53,6 +53,7 @@ include:
- path: UITests/SupportingFiles/target.yml - path: UITests/SupportingFiles/target.yml
- path: IntegrationTests/SupportingFiles/target.yml - path: IntegrationTests/SupportingFiles/target.yml
- path: NSE/SupportingFiles/target.yml - path: NSE/SupportingFiles/target.yml
- path: ShareExtension/SupportingFiles/target.yml
# - path: NCE/SupportingFiles/target.yml (not used yet) # - path: NCE/SupportingFiles/target.yml (not used yet)
# - path: MyAppVariant/override.yml # - path: MyAppVariant/override.yml