mirror of
https://github.com/element-hq/element-x-ios.git
synced 2025-03-10 21:39:12 +00:00
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:
parent
3a600a94d5
commit
b122b02bee
@ -22,6 +22,7 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
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 */; };
|
||||
0180C44B997EDA8D21F883AC /* RoomNotificationSettingsCustomSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B746EFA112532A7B701FB914 /* RoomNotificationSettingsCustomSectionView.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 */; };
|
||||
037006FB6DF1374F94E4058D /* Dictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.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 */; };
|
||||
0437765FF480249486893CC7 /* ScreenTrackerViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.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 */; };
|
||||
05BAB510CBC2ED35C154ADD0 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFD012C3A9F5EF276DDD4AA /* AnalyticsPromptScreenViewModelProtocol.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 */; };
|
||||
069358C2C825A19DE6CB127E /* TracingConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */; };
|
||||
06B31F84CE52A7A7C271267C /* SecureBackupRecoveryKeyScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */; };
|
||||
06B55882911B4BF5B14E9851 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 227AC5D71A4CE43512062243 /* URL.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 */; };
|
||||
1583E2D766E4485FF91662FC /* PermalinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3EB5B1848CF4F64E63C6B7 /* PermalinkTests.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 */; };
|
||||
167D00CAA13FAFB822298021 /* MediaProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.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 */; };
|
||||
18867F4F1C8991EEC56EA932 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.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 */; };
|
||||
1950A80CD198BED283DFC2CE /* ClientProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */; };
|
||||
197441F1EF23A5DABACCA79F /* StickerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5338450E6783A576B5C16DD /* StickerRoomTimelineView.swift */; };
|
||||
19DED23340D0855B59693ED2 /* VoiceMessageRecorderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D45C9EAA86423D7D3126DE4F /* VoiceMessageRecorderProtocol.swift */; };
|
||||
19DF5600A7F547B22DD7872A /* CompletionSuggestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A12D3D8138F1B71AFA7C858 /* CompletionSuggestionService.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 */; };
|
||||
1A70A2199394B5EC660934A5 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = A678E40E917620059695F067 /* MatrixRustSDK */; };
|
||||
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 */; };
|
||||
260FFC1475EE94F641C3F3F9 /* PollFormScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40F1985065500F0E7F61A27 /* PollFormScreenViewModelProtocol.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 */; };
|
||||
273AB64B9A26B61C51858867 /* AsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A73A07BAEDD74C48795A996A /* AsyncSequence.swift */; };
|
||||
274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97CE98208321C4D66E363612 /* ShimmerModifier.swift */; };
|
||||
275EDE8849A2AC1D9309ED7C /* TemplateScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B43456E73F8A2D52B69B9FB9 /* TemplateScreenViewModel.swift */; };
|
||||
2797C9D9BA642370F1C85D78 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; };
|
||||
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 */; };
|
||||
281BED345D59A9A6A99E9D98 /* UNNotificationContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE148A4FFEE853C5A281500C /* UNNotificationContent.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 */; };
|
||||
2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B10423B9102086A2D9BFCBA /* EventTimelineItem.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 */; };
|
||||
2F623DA1122140A987B34D08 /* NotificationSettingsEditScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA7BB497B2F539C17E88F6B7 /* NotificationSettingsEditScreenViewModelProtocol.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 */; };
|
||||
3582056513A384F110EC8274 /* MediaPlayerProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7A2C4A3A74F0D2FFE9356A /* MediaPlayerProviderTests.swift */; };
|
||||
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 */; };
|
||||
36926D795D6D19177C7812F8 /* EncryptionResetPasswordScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6935A55AB3B0C94BC566DD6 /* EncryptionResetPasswordScreenCoordinator.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 */; };
|
||||
3982E60F9C126437D5E488A3 /* PillContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A6314FDC51DA25712D9A81 /* PillContextTests.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 */; };
|
||||
3A164187907DA43B7858F9EC /* CompletionSuggestionServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EA0312A6262484AA393AC9 /* CompletionSuggestionServiceTests.swift */; };
|
||||
3A64A93A651A3CB8774ADE8E /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = BA93CD75CCE486660C9040BD /* Collections */; };
|
||||
@ -360,6 +373,7 @@
|
||||
4DAEE2468669848B6C9F55B4 /* TimelineReadReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33035418BB35754232985871 /* TimelineReadReceiptsView.swift */; };
|
||||
4DEEFB73181C3B023DB42686 /* NetworkMonitorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1575947B7A6FE08C57FE5EE4 /* NetworkMonitorProtocol.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 */; };
|
||||
4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.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 */; };
|
||||
53DEF39F0C4DE02E3FC56D91 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; };
|
||||
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 */; };
|
||||
54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */; };
|
||||
5518DA4A6C9B4FC4B497EA9A /* LogViewerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01B795AAAB7B8747FE2FF311 /* LogViewerScreenModels.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 */; };
|
||||
55D18AA4F4A2257642EBDB94 /* GlobalSearchScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38354164AF59C5006CD05878 /* GlobalSearchScreenViewModel.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 */; };
|
||||
59C41313AED7566C3AC51163 /* RoomSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29A953B6C0C431DBF4DD00B4 /* RoomSummary.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 */; };
|
||||
5B6E5AD224509E6C0B520D6E /* RoomMemberDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7DDF49CEBC0DFC59C308335F /* RoomMemberDetailsScreenViewModelProtocol.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 */; };
|
||||
5D56CE09743C6B90C21B04C2 /* RoomMembersListScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E9E0929CEFA356090BE5FB8 /* RoomMembersListScreenViewModelTests.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 */; };
|
||||
5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.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 */; };
|
||||
663E198678778F7426A9B27D /* Collection.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FAFE1C2149E6AC8156ED2B /* Collection.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 */; };
|
||||
6786C4B0936AC84D993B20BF /* NotificationSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F06F2F09B2EDD067DC2174 /* NotificationSettingsScreen.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 */; };
|
||||
7A71AEF419904209BB8C2833 /* UserAgentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2529D434C750ED78ADF1ED /* UserAgentBuilder.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 */; };
|
||||
7B3A59786DB2F741A1743ED0 /* PinnedEventsTimelineScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510E89B989477E5EE8E503C0 /* PinnedEventsTimelineScreenViewModelProtocol.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 */; };
|
||||
88F348E2CB14FF71CBBB665D /* AudioRoomTimelineItemContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7475C5AE20BA896930907EA8 /* AudioRoomTimelineItemContent.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 */; };
|
||||
89658A44C9FC19B58FD1C226 /* ServerConfirmationScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.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 */; };
|
||||
8D71E5E53F372202379BECCE /* BugReportScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 303FCADE77DF1F3670C086ED /* BugReportScreenViewModel.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 */; };
|
||||
8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.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 */; };
|
||||
962A4F8AD6312804E2C6BB6E /* PhotoLibraryPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A232D9156D225BD9FD1D0C43 /* PhotoLibraryPicker.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 */; };
|
||||
96B3606E30F824095B1DD022 /* NetworkMonitorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8DA1E8F287680C8ED25EDBAC /* NetworkMonitorMock.swift */; };
|
||||
97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; };
|
||||
@ -848,6 +869,7 @@
|
||||
B818580464CFB5400A3EF6AE /* TimelineModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 029D5701F80A9AF7167BB4D0 /* TimelineModels.swift */; };
|
||||
B879446FD8E65A711EF8F9F7 /* AdvancedSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.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 */; };
|
||||
B93FA0DA1504B301CAEE141B /* NotificationSettingsProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6F5D66F158A6662F953733E /* NotificationSettingsProxy.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 */; };
|
||||
BDC4EB54CC3036730475CB8B /* QRCodeLoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25E7E9B7FEAB6169D960C206 /* QRCodeLoginScreenViewModelTests.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 */; };
|
||||
BFEB24336DFD5F196E6F3456 /* IntentionalMentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF5CBAF69BDF5DF31C661E1 /* IntentionalMentions.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 */; };
|
||||
C08AAE7563E0722C9383F51C /* RoomMembersListScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B8E176484A89BAC389D4076 /* RoomMembersListScreen.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 */; };
|
||||
D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.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 */; };
|
||||
D8F1462EA00AFC939FF9ACCA /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 203D1ACC20287F8986C959D3 /* target.yml */; };
|
||||
D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; };
|
||||
D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.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 */; };
|
||||
DB65401349C143DFF883E2B0 /* AnalyticsPromptScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8EC6EA7EDFCE46710DA306 /* AnalyticsPromptScreenViewModel.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 */; };
|
||||
F18CA61A58C77C84F551B8E7 /* GeneratedMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57916A1578D8043BB0795441 /* GeneratedMocks.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 */; };
|
||||
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 */; };
|
||||
F3ECA377FF77E81A4F1FA062 /* TimelineItemSendInfoLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753B4C6C0EDDCBF0708DC384 /* TimelineItemSendInfoLabel.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 */; };
|
||||
F656F92A63D3DC1978D79427 /* Algorithms in Frameworks */ = {isa = PBXBuildFile; productRef = 290FDEDA4D764B9F7EBE55A9 /* Algorithms */; };
|
||||
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 */; };
|
||||
F697284B9B5F2C00CFEA3B12 /* EmojiDetectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A58E93D91DE3288010390DEE /* EmojiDetectionTests.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 */; };
|
||||
FD762761C5D0C30E6255C3D8 /* ServerConfirmationScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.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 */; };
|
||||
FEC03105D1BDE0F49BD7F243 /* PinnedEventsTimelineScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B6572E6EF5D5F4B0C338A40 /* PinnedEventsTimelineScreenModels.swift */; };
|
||||
FEFD5290B31FCBA6999912C8 /* RoomChangePermissionsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */; };
|
||||
@ -1173,6 +1202,13 @@
|
||||
remoteGlobalIDString = C0FAEB81CFD9776CD78CE489;
|
||||
remoteInfo = ElementX;
|
||||
};
|
||||
52A426E590105174D83B9532 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = AC22997D58D612146053154D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 19F0C845D67E9BEA4BE7133E;
|
||||
remoteInfo = ShareExtension;
|
||||
};
|
||||
6848AF4480814C5F810FB7EB /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = AC22997D58D612146053154D /* Project object */;
|
||||
@ -1204,6 +1240,7 @@
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
EB88DBD77221E2CFE463018C /* NSE.appex in Embed Foundation Extensions */,
|
||||
00C3023B6DF55024D8876B76 /* ShareExtension.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -1334,6 +1371,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1348,6 +1386,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1358,6 +1397,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1484,15 +1524,18 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1512,6 +1555,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1581,6 +1625,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -1788,6 +1833,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2034,6 +2080,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2068,6 +2115,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2150,7 +2198,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2173,6 +2220,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2187,6 +2235,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2205,7 +2254,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2284,6 +2332,7 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@ -2355,6 +2404,16 @@
|
||||
);
|
||||
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 */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -2495,6 +2554,7 @@
|
||||
06501F0E978B2D5C92771DC7 /* Logging */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
41A8571A8A071FB41778C016 /* ExtensionLogger.swift */,
|
||||
111B698739E3410E2CDB7144 /* MXLog.swift */,
|
||||
542D4F49FABA056DEEEB3400 /* RustTracing.swift */,
|
||||
ED003DF1B7CF40E7073A2280 /* TracingConfiguration.swift */,
|
||||
@ -2866,6 +2926,18 @@
|
||||
path = View;
|
||||
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 */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3221,10 +3293,21 @@
|
||||
823ED0EC3F1B6CF47D284011 /* Tools */,
|
||||
B04B538A859CD012755DC19C /* NSE */,
|
||||
1803CD2B96BF06009334BB61 /* PreviewTests */,
|
||||
D0111119CDF3E28E6D7768E8 /* ShareExtension */,
|
||||
681566846AF307E9BA4C72C6 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
40D9A816C45E0278C29DF883 /* SupportingFiles */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
505ADA084C0B38A0C4AD2574 /* Info.plist */,
|
||||
B88CE0A058727BC68EEEC6B6 /* ShareExtension.entitlements */,
|
||||
203D1ACC20287F8986C959D3 /* target.yml */,
|
||||
);
|
||||
path = SupportingFiles;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
40E6246F03D1FE377BC5D963 /* Room */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -3518,11 +3601,18 @@
|
||||
path = BugReportScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
557C534BD2052BFFD810CE3D /* ShareExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DCAC01A97A43BE07B9E94E43 /* ShareExtensionModels.swift */,
|
||||
);
|
||||
path = ShareExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
566F2B84465726112B830CF6 /* Other */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4959CECEC984B3995616F427 /* DataProtectionManager.swift */,
|
||||
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */,
|
||||
EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */,
|
||||
49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */,
|
||||
);
|
||||
@ -3698,6 +3788,7 @@
|
||||
9C7F7DE62D33C6A26CBFCD72 /* IntegrationTests.xctest */,
|
||||
0D8F620C8B314840D8602E3F /* NSE.appex */,
|
||||
D95E8C0EFEC0C6F96EDAA71A /* PreviewTests.xctest */,
|
||||
3D8BEEFCA07BEA43F4F4BF77 /* ShareExtension.appex */,
|
||||
F506C6ADB1E1DA6638078E11 /* UITests.xctest */,
|
||||
AAC9344689121887B74877AF /* UnitTests.xctest */,
|
||||
);
|
||||
@ -4144,6 +4235,14 @@
|
||||
path = Replies;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7FF02C3DED8CD9890375D9FF /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3C3ADF21BE301D0DA48F2A7E /* ShareExtensionView.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
8039515BAA53B7C3275AC64A /* Client */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -4908,7 +5007,7 @@
|
||||
children = (
|
||||
04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */,
|
||||
3D65BCC659FD9087E49B3C25 /* AppAppearance.swift */,
|
||||
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */,
|
||||
C142248014E08E885E323E56 /* Avatars.swift */,
|
||||
E5272BC4A60B6AD7553BACA1 /* BlurHashDecode.swift */,
|
||||
9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */,
|
||||
AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */,
|
||||
@ -5105,6 +5204,15 @@
|
||||
path = Layout;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D0111119CDF3E28E6D7768E8 /* ShareExtension */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F08E29610C82E4201463C4A5 /* Sources */,
|
||||
40D9A816C45E0278C29DF883 /* SupportingFiles */,
|
||||
);
|
||||
path = ShareExtension;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D382E465AF067C1BF888BF8E /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -5295,6 +5403,7 @@
|
||||
D57B3BC211BB74420C9138D7 /* RoomPollsHistoryScreen */,
|
||||
7B890CCD20B037760BFDF957 /* RoomRolesAndPermissionsScreen */,
|
||||
679E9837ECA8D6776079D16E /* RoomScreen */,
|
||||
2E42D43DB6835A58D88B2F91 /* RoomSelectionScreen */,
|
||||
2565414373E6F68005966B8E /* SecureBackup */,
|
||||
70B74A432C241E56A7ACE610 /* Settings */,
|
||||
EC4545C7E37E8294D3FE6800 /* StartChatScreen */,
|
||||
@ -5334,6 +5443,7 @@
|
||||
22F9F1514B91803BB4B88894 /* AppHooks */,
|
||||
337015ADFBA3AB96660DB3A6 /* Generated */,
|
||||
31CE4DA53232AA534057F912 /* Mocks */,
|
||||
557C534BD2052BFFD810CE3D /* ShareExtension */,
|
||||
4C826614718790C58C17117F /* UnitTests */,
|
||||
);
|
||||
path = Sources;
|
||||
@ -5426,6 +5536,15 @@
|
||||
path = BlockedUsersScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F08E29610C82E4201463C4A5 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3D27299A36536DBF91AE8FA6 /* ShareExtensionViewController.swift */,
|
||||
7FF02C3DED8CD9890375D9FF /* View */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
F12966DF3DA87FEF21348D60 /* InviteUsersScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -5567,6 +5686,14 @@
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FF654D7FD6693839E3185FAD /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D879DC5515B1D42577F96C94 /* RoomSelectionScreen.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FFD7C58CA6A7D6BBC2F584B5 /* JoinRoomScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -5616,6 +5743,28 @@
|
||||
productReference = F506C6ADB1E1DA6638078E11 /* UITests.xctest */;
|
||||
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 */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 79663128986C62EFAC289176 /* Build configuration list for PBXNativeTarget "UnitTests" */;
|
||||
@ -5672,6 +5821,7 @@
|
||||
);
|
||||
dependencies = (
|
||||
2C29670603B37E38705D5FF1 /* PBXTargetDependency */,
|
||||
58C473A5DEA945AACFEA8E9F /* PBXTargetDependency */,
|
||||
);
|
||||
name = ElementX;
|
||||
packageProductDependencies = (
|
||||
@ -5774,6 +5924,9 @@
|
||||
DevelopmentTeam = 7J4U792NQT;
|
||||
TestTargetID = C0FAEB81CFD9776CD78CE489;
|
||||
};
|
||||
19F0C845D67E9BEA4BE7133E = {
|
||||
DevelopmentTeam = "$(DEVELOPMENT_TEAM)";
|
||||
};
|
||||
32C23C8D224D46EFE62AFAD0 = {
|
||||
DevelopmentTeam = 7J4U792NQT;
|
||||
};
|
||||
@ -5862,6 +6015,7 @@
|
||||
FEB53A5BC378C913769656D8 /* NSE */,
|
||||
F8E276FD6DC43EADB85241BC /* Periphery */,
|
||||
7A17BE29BAC81ADBAC6349D9 /* PreviewTests */,
|
||||
19F0C845D67E9BEA4BE7133E /* ShareExtension */,
|
||||
0E28CD62691FDBC63147D5E3 /* UITests */,
|
||||
32C23C8D224D46EFE62AFAD0 /* UnitTests */,
|
||||
);
|
||||
@ -5869,6 +6023,14 @@
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
087F14F27D0A6FDFB80392A1 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D8F1462EA00AFC939FF9ACCA /* target.yml in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
215E1D91B98672C856F559D0 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -6071,7 +6233,7 @@
|
||||
484202C5D50983442D24D061 /* AttributedString.swift in Sources */,
|
||||
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */,
|
||||
BA43D782BE85C7F5F20C624A /* AttributedStringBuilderProtocol.swift in Sources */,
|
||||
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */,
|
||||
F255083E18CDBFDF7E640FB1 /* Avatars.swift in Sources */,
|
||||
9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */,
|
||||
238D561CA231339C6D4D06F3 /* ClientBuilder.swift in Sources */,
|
||||
0BAF83521871E69D222EE8E4 /* ClientBuilderHook.swift in Sources */,
|
||||
@ -6081,6 +6243,7 @@
|
||||
24A75F72EEB7561B82D726FD /* Date.swift in Sources */,
|
||||
9F11B9F347F9E2D236799FB3 /* ElementCallServiceConstants.swift in Sources */,
|
||||
CFEC53440C572CEEABC4A6A0 /* ElementXAttributeScope.swift in Sources */,
|
||||
89198AE2649DD77673D5793B /* ExtensionLogger.swift in Sources */,
|
||||
A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */,
|
||||
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */,
|
||||
EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */,
|
||||
@ -6095,7 +6258,6 @@
|
||||
9DD5AA10E85137140FEA86A3 /* MediaProvider.swift in Sources */,
|
||||
7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */,
|
||||
E2DB696117BAEABAD5718023 /* MediaSourceProxy.swift in Sources */,
|
||||
5455147CAC63F71E48F7D699 /* NSELogger.swift in Sources */,
|
||||
30CC4F796B27BE8B1DFDBF5A /* NSEUserSession.swift in Sources */,
|
||||
1D5DC685CED904386C89B7DA /* NSRegularExpresion.swift in Sources */,
|
||||
94F0B78928E952689ACDB271 /* NetworkMonitor.swift in Sources */,
|
||||
@ -6267,6 +6429,30 @@
|
||||
);
|
||||
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 */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -6371,7 +6557,7 @@
|
||||
874FEFB9D4A4AF447E0E086E /* AuthenticationStartScreenViewModelProtocol.swift in Sources */,
|
||||
6146996D5C4DDD5DA816FC87 /* AuthenticationTextFieldStyle.swift in Sources */,
|
||||
4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */,
|
||||
D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */,
|
||||
1621BF6316FFFEF5AE067C77 /* Avatars.swift in Sources */,
|
||||
7A25D6926A2C01DB8D0D67A5 /* BadgeLabel.swift in Sources */,
|
||||
A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */,
|
||||
FC0EEFF630F34899953BB950 /* BigIcon.swift in Sources */,
|
||||
@ -6509,6 +6695,7 @@
|
||||
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */,
|
||||
2F09DF0CB213CAE86A3E3B67 /* EventTimelineItem.swift in Sources */,
|
||||
63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */,
|
||||
36206F74DDEBF9BEAF6A6A1F /* ExtensionLogger.swift in Sources */,
|
||||
5F06AD3C66884CE793AE6119 /* FileManager.swift in Sources */,
|
||||
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */,
|
||||
37D789F24199B32E3FD1AA7B /* FileRoomTimelineItemContent.swift in Sources */,
|
||||
@ -6860,6 +7047,11 @@
|
||||
352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */,
|
||||
7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.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 */,
|
||||
59C41313AED7566C3AC51163 /* RoomSummary.swift in Sources */,
|
||||
983896D611ABF52A5C37498D /* RoomSummaryProvider.swift in Sources */,
|
||||
@ -6945,6 +7137,7 @@
|
||||
B93D7CE520088AD53FA6D53C /* SettingsScreenModels.swift in Sources */,
|
||||
E0B6A569AC3E81D233B43D60 /* SettingsScreenViewModel.swift in Sources */,
|
||||
A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */,
|
||||
5D99F63CC88BB29383019FC6 /* ShareExtensionModels.swift in Sources */,
|
||||
1C8BC70A18060677E295A846 /* ShareToMapsAppActivity.swift in Sources */,
|
||||
274CE3C986841D15FD530BF5 /* ShimmerModifier.swift in Sources */,
|
||||
77920AFA8091AC6B9F190C90 /* Signposter.swift in Sources */,
|
||||
@ -7174,6 +7367,11 @@
|
||||
target = C0FAEB81CFD9776CD78CE489 /* ElementX */;
|
||||
targetProxy = 6848AF4480814C5F810FB7EB /* PBXContainerItemProxy */;
|
||||
};
|
||||
58C473A5DEA945AACFEA8E9F /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 19F0C845D67E9BEA4BE7133E /* ShareExtension */;
|
||||
targetProxy = 52A426E590105174D83B9532 /* PBXContainerItemProxy */;
|
||||
};
|
||||
8E24DC048A099AAFEE13B4F5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = C0FAEB81CFD9776CD78CE489 /* ElementX */;
|
||||
@ -7445,6 +7643,27 @@
|
||||
};
|
||||
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 */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -7697,6 +7916,27 @@
|
||||
};
|
||||
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 */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@ -7754,6 +7994,15 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
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" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@ -8068,6 +8317,11 @@
|
||||
package = EC6D0C817B1C21D9D096505A /* XCRemoteSwiftPackageReference "Version" */;
|
||||
productName = Version;
|
||||
};
|
||||
3262F08E1C3483C22A7A319F /* Compound */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = F71C70A4404CC6D9C4AF35F2 /* XCRemoteSwiftPackageReference "compound-ios" */;
|
||||
productName = Compound;
|
||||
};
|
||||
36B7FC232711031AA2B0D188 /* DTCoreText */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */;
|
||||
@ -8253,11 +8507,21 @@
|
||||
package = 6FC4820D8D4559CEECA064D7 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */;
|
||||
productName = MatrixRustSDK;
|
||||
};
|
||||
C79D91A7F9F378CECEF64B5A /* MatrixRustSDK */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 6FC4820D8D4559CEECA064D7 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */;
|
||||
productName = MatrixRustSDK;
|
||||
};
|
||||
CA07D57389DACE18AEB6A5E2 /* WysiwygComposer */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = EE40B0E16A55BD23ECBFFD22 /* XCRemoteSwiftPackageReference "matrix-rich-text-editor-swift" */;
|
||||
productName = WysiwygComposer;
|
||||
};
|
||||
CAA3B9DF998B397C9EE64E8B /* Collections */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */;
|
||||
productName = Collections;
|
||||
};
|
||||
CCE5BF78B125320CBF3BB834 /* PostHog */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
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 {
|
||||
handleAppRoute(.childEventOnRoomAlias(eventID: eventID, alias: alias))
|
||||
}
|
||||
case .share:
|
||||
guard isExternalURL else {
|
||||
MXLog.error("Received unexpected internal share route")
|
||||
break
|
||||
}
|
||||
handleAppRoute(route)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum AppRoute: Equatable {
|
||||
enum AppRoute: Equatable, Hashable {
|
||||
/// The app's home screen.
|
||||
case roomList
|
||||
/// A room, shown as the root of the stack (popping any child rooms).
|
||||
@ -41,6 +41,8 @@ enum AppRoute: Equatable {
|
||||
case settings
|
||||
/// The setting screen for key backup.
|
||||
case chatBackupSettings
|
||||
/// An external share request e.g. from the ShareExtension
|
||||
case share(ShareExtensionPayload)
|
||||
}
|
||||
|
||||
struct AppRouteURLParser {
|
||||
@ -48,6 +50,7 @@ struct AppRouteURLParser {
|
||||
|
||||
init(appSettings: AppSettings) {
|
||||
urlParsers = [
|
||||
AppGroupURLParser(),
|
||||
MatrixPermalinkParser(),
|
||||
ElementWebURLParser(domains: appSettings.elementWebHosts),
|
||||
ElementCallURLParser()
|
||||
@ -73,6 +76,30 @@ protocol URLParser {
|
||||
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`.
|
||||
struct ElementCallURLParser: URLParser {
|
||||
private let knownHosts = ["call.element.io"]
|
||||
|
@ -83,7 +83,7 @@ class EncryptionSettingsFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case .roomList, .room, .roomAlias, .childRoom, .childRoomAlias,
|
||||
.roomDetails, .roomMemberDetails, .userProfile,
|
||||
.event, .eventOnRoomAlias, .childEvent, .childEventOnRoomAlias,
|
||||
.call, .genericCallLink, .settings:
|
||||
.call, .genericCallLink, .settings, .share:
|
||||
// These routes aren't in this flow so clear the entire stack.
|
||||
clearRoute(animated: animated)
|
||||
case .chatBackupSettings:
|
||||
|
@ -293,9 +293,7 @@ class OnboardingFlowCoordinator: FlowCoordinatorProtocol {
|
||||
let coordinator = SecureBackupRecoveryKeyScreenCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.actions
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
.sink { action in
|
||||
switch action {
|
||||
case .complete:
|
||||
break // Moving to next state is Handled by the global session verification listener
|
||||
|
@ -65,7 +65,9 @@ class PinnedEventsTimelineFlowCoordinator: FlowCoordinatorProtocol {
|
||||
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
|
||||
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")
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,8 @@ enum RoomFlowCoordinatorEntryPoint: Hashable {
|
||||
case eventID(String)
|
||||
/// The flow will start by showing the room's details.
|
||||
case roomDetails
|
||||
/// An external media share request
|
||||
case share(ShareExtensionPayload)
|
||||
|
||||
var isEventID: Bool {
|
||||
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
|
||||
class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
private let roomID: String
|
||||
@ -112,6 +140,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
fatalError("This flow coordinator expect a route")
|
||||
}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
func handleAppRoute(_ appRoute: AppRoute, animated: Bool) {
|
||||
guard stateMachine.state != .complete else {
|
||||
fatalError("This flow coordinator is `finished` ☠️")
|
||||
@ -152,7 +181,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.presentRoomMemberDetails(userID: userID), userInfo: EventUserInfo(animated: animated))
|
||||
}
|
||||
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):
|
||||
if case .presentingChild = stateMachine.state, let childRoomFlowCoordinator {
|
||||
childRoomFlowCoordinator.handleAppRoute(appRoute, animated: animated)
|
||||
@ -161,6 +195,21 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
} else {
|
||||
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:
|
||||
break // These are converted to a room ID route one level above.
|
||||
case .roomList, .userProfile, .call, .genericCallLink, .settings, .chatBackupSettings:
|
||||
@ -176,7 +225,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
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 let room = await userSession.clientProxy.roomForIdentifier(roomID) else {
|
||||
@ -187,8 +236,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
switch room {
|
||||
case .joined(let roomProxy):
|
||||
await storeAndSubscribeToRoomProxy(roomProxy)
|
||||
let focussedEvent = focussedEventID.map { FocusEvent(eventID: $0, shouldSetPin: false) }
|
||||
stateMachine.tryEvent(.presentRoom(focussedEvent: focussedEvent), userInfo: EventUserInfo(animated: animated))
|
||||
stateMachine.tryEvent(.presentRoom(presentationAction: presentationAction), userInfo: EventUserInfo(animated: animated))
|
||||
default:
|
||||
stateMachine.tryEvent(.presentJoinRoomScreen(via: via), userInfo: EventUserInfo(animated: animated))
|
||||
}
|
||||
@ -376,9 +424,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
presentJoinRoomScreen(via: via, animated: true)
|
||||
case (_, .dismissJoinRoomScreen, .complete):
|
||||
dismissFlow(animated: animated)
|
||||
|
||||
case (_, .presentRoom(let focussedEvent), .room):
|
||||
Task { await self.presentRoom(fromState: context.fromState, focussedEvent: focussedEvent, animated: animated) }
|
||||
|
||||
case (_, .presentRoom(let presentationAction), .room):
|
||||
Task {
|
||||
await self.presentRoom(fromState: context.fromState,
|
||||
presentationAction: presentationAction,
|
||||
animated: animated)
|
||||
}
|
||||
case (_, .dismissFlow, .complete):
|
||||
dismissFlow(animated: animated)
|
||||
|
||||
@ -445,9 +497,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
break
|
||||
|
||||
case (.mediaUploadPicker, .presentMediaUploadPreview, .mediaUploadPreview(let fileURL)):
|
||||
presentMediaUploadPreviewScreen(for: fileURL)
|
||||
presentMediaUploadPreviewScreen(for: fileURL, animated: animated)
|
||||
case (.room, .presentMediaUploadPreview, .mediaUploadPreview(let fileURL)):
|
||||
presentMediaUploadPreviewScreen(for: fileURL)
|
||||
presentMediaUploadPreviewScreen(for: fileURL, animated: animated)
|
||||
case (.mediaUploadPreview, .dismissMediaUploadPreview, .room):
|
||||
break
|
||||
|
||||
@ -542,7 +594,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
/// - 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
|
||||
/// - 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
|
||||
// through the correct states before presenting the room
|
||||
navigationStackCoordinator.setSheetCoordinator(nil)
|
||||
@ -559,9 +611,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
default:
|
||||
// The room is already on the stack, no need to present it again
|
||||
|
||||
// Check if we need to focus on an event
|
||||
if let focussedEvent {
|
||||
roomScreenCoordinator?.focusOnEvent(focussedEvent)
|
||||
switch presentationAction {
|
||||
case .eventFocus(let focusedEvent):
|
||||
roomScreenCoordinator?.focusOnEvent(focusedEvent)
|
||||
case .share(.mediaFile(_, let mediaFile)):
|
||||
stateMachine.tryEvent(.presentMediaUploadPreview(fileURL: mediaFile.url))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return
|
||||
@ -580,8 +636,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateEventStringBuilder: RoomStateEventStringBuilder(userID: userID))
|
||||
|
||||
let timelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy,
|
||||
initialFocussedEventID: focussedEvent?.eventID,
|
||||
timelineItemFactory: timelineItemFactory)
|
||||
initialFocussedEventID: presentationAction?.focusedEvent?.eventID,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
self.timelineController = timelineController
|
||||
|
||||
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,
|
||||
roomProxy: roomProxy,
|
||||
focussedEvent: focussedEvent,
|
||||
focussedEvent: presentationAction?.focusedEvent,
|
||||
timelineController: timelineController,
|
||||
mediaProvider: userSession.mediaProvider,
|
||||
mediaPlayerProvider: MediaPlayerProvider(),
|
||||
@ -655,6 +712,13 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
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) {
|
||||
@ -679,7 +743,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
if case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomID) {
|
||||
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,
|
||||
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 parameters = MediaUploadPreviewScreenCoordinatorParameters(userIndicatorController: userIndicatorController,
|
||||
@ -918,7 +982,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
stackCoordinator.setRootCoordinator(mediaUploadPreviewScreenCoordinator)
|
||||
|
||||
navigationStackCoordinator.setSheetCoordinator(stackCoordinator) { [weak self] in
|
||||
navigationStackCoordinator.setSheetCoordinator(stackCoordinator, animated: animated) { [weak self] in
|
||||
self?.stateMachine.tryEvent(.dismissMediaUploadPreview)
|
||||
}
|
||||
}
|
||||
@ -1091,7 +1155,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
|
||||
let roomTimelineController = roomTimelineControllerFactory.buildRoomTimelineController(roomProxy: roomProxy,
|
||||
initialFocussedEventID: nil,
|
||||
timelineItemFactory: timelineItemFactory)
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
let parameters = RoomPollsHistoryScreenCoordinatorParameters(pollInteractionHandler: PollInteractionHandler(analyticsService: analytics, roomProxy: roomProxy),
|
||||
roomTimelineController: roomTimelineController)
|
||||
@ -1361,7 +1426,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
stateMachine.tryEvent(.startChildFlow(roomID: roomID, via: [], entryPoint: .room))
|
||||
case .displayRoomScreenWithFocussedPin(let eventID):
|
||||
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)
|
||||
@ -1427,6 +1492,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
||||
coordinator.handleAppRoute(.event(eventID: eventID, roomID: roomID, via: via), animated: true)
|
||||
case .roomDetails:
|
||||
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 dismissJoinRoomScreen
|
||||
|
||||
case presentRoom(focussedEvent: FocusEvent?)
|
||||
case presentRoom(presentationAction: PresentationAction?)
|
||||
case dismissFlow
|
||||
|
||||
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)
|
||||
case .settings, .chatBackupSettings:
|
||||
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 .roomDetails: .roomDetails(roomID: roomID)
|
||||
case .eventID(let eventID): .event(eventID: eventID, roomID: roomID, via: via) // ignored.
|
||||
case .share(let payload): .share(payload)
|
||||
}
|
||||
roomFlowCoordinator.handleAppRoute(route, animated: animated)
|
||||
} else {
|
||||
@ -296,6 +309,12 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
case (.userProfileScreen, .dismissedUserProfileScreen, .roomList):
|
||||
break
|
||||
|
||||
case (.roomList, .showShareExtensionRoomList, .shareExtensionRoomList(let sharePayload)):
|
||||
clearRoute(animated: animated)
|
||||
presentRoomSelectionScreen(sharePayload: sharePayload, animated: animated)
|
||||
case (.shareExtensionRoomList, .dismissedShareExtensionRoomList, .roomList):
|
||||
dismissRoomSelectionScreen()
|
||||
|
||||
default:
|
||||
fatalError("Unknown transition: \(context)")
|
||||
}
|
||||
@ -583,6 +602,8 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
|
||||
coordinator.handleAppRoute(.event(eventID: eventID, roomID: roomID, via: via), animated: animated)
|
||||
case .roomDetails:
|
||||
coordinator.handleAppRoute(.roomDetails(roomID: roomID), animated: animated)
|
||||
case .share(let payload):
|
||||
coordinator.handleAppRoute(.share(payload), animated: animated)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
private static let loadingIndicatorIdentifier = "\(UserSessionFlowCoordinator.self)-Loading"
|
||||
|
@ -42,10 +42,12 @@ class UserSessionFlowCoordinatorStateMachine {
|
||||
/// Showing the user profile screen. This screen clears the navigation.
|
||||
case userProfileScreen
|
||||
|
||||
case shareExtensionRoomList(sharePayload: ShareExtensionPayload)
|
||||
|
||||
/// The selected room ID from the state if available.
|
||||
var selectedRoomID: String? {
|
||||
switch self {
|
||||
case .initial, .userProfileScreen:
|
||||
case .initial, .userProfileScreen, .shareExtensionRoomList:
|
||||
nil
|
||||
case .roomList(let selectedRoomID),
|
||||
.feedbackScreen(let selectedRoomID),
|
||||
@ -116,6 +118,9 @@ class UserSessionFlowCoordinatorStateMachine {
|
||||
case showUserProfileScreen(userID: String)
|
||||
/// The user profile screen has been dismissed.
|
||||
case dismissedUserProfileScreen
|
||||
|
||||
case showShareExtensionRoomList(sharePayload: ShareExtensionPayload)
|
||||
case dismissedShareExtensionRoomList
|
||||
}
|
||||
|
||||
private let stateMachine: StateMachine<State, Event>
|
||||
@ -184,6 +189,11 @@ class UserSessionFlowCoordinatorStateMachine {
|
||||
case (.userProfileScreen, .dismissedUserProfileScreen):
|
||||
return .roomList(selectedRoomID: nil)
|
||||
|
||||
case (.roomList, .showShareExtensionRoomList(let sharePayload)):
|
||||
return .shareExtensionRoomList(sharePayload: sharePayload)
|
||||
case (.shareExtensionRoomList, .dismissedShareExtensionRoomList):
|
||||
return .roomList(selectedRoomID: nil)
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
@ -12939,15 +12939,15 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
|
||||
|
||||
//MARK: - buildRoomTimelineController
|
||||
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount = 0
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount: Int {
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount
|
||||
returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -12955,29 +12955,29 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount = newValue
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingCallsCount = newValue
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCalled: Bool {
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount > 0
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCalled: Bool {
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount > 0
|
||||
}
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol)?
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol)] = []
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)?
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = []
|
||||
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue: RoomTimelineControllerProtocol!
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReturnValue: RoomTimelineControllerProtocol! {
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue: RoomTimelineControllerProtocol!
|
||||
var buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue: RoomTimelineControllerProtocol! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: RoomTimelineControllerProtocol? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue
|
||||
returnValue = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -12985,39 +12985,39 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryUnderlyingReturnValue = newValue
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
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 {
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount += 1
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments = (roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory)
|
||||
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol, initialFocussedEventID: String?, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol {
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount += 1
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)
|
||||
DispatchQueue.main.async {
|
||||
self.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedInvocations.append((roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory))
|
||||
self.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, initialFocussedEventID: initialFocussedEventID, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider))
|
||||
}
|
||||
if let buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryClosure = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryClosure {
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryClosure(roomProxy, initialFocussedEventID, timelineItemFactory)
|
||||
if let buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure = buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure {
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderClosure(roomProxy, initialFocussedEventID, timelineItemFactory, mediaProvider)
|
||||
} else {
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReturnValue
|
||||
return buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue
|
||||
}
|
||||
}
|
||||
//MARK: - buildRoomPinnedTimelineController
|
||||
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount = 0
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCallsCount: Int {
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = 0
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount
|
||||
returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -13025,29 +13025,29 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount = newValue
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingCallsCount = newValue
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCalled: Bool {
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCallsCount > 0
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCalled: Bool {
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount > 0
|
||||
}
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol)?
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol)] = []
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments: (roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)?
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations: [(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol)] = []
|
||||
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue: RoomTimelineControllerProtocol?
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReturnValue: RoomTimelineControllerProtocol? {
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue: RoomTimelineControllerProtocol?
|
||||
var buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue: RoomTimelineControllerProtocol? {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: RoomTimelineControllerProtocol?? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue
|
||||
returnValue = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
@ -13055,26 +13055,26 @@ class RoomTimelineControllerFactoryMock: RoomTimelineControllerFactoryProtocol {
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryUnderlyingReturnValue = newValue
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
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? {
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryCallsCount += 1
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedArguments = (roomProxy: roomProxy, timelineItemFactory: timelineItemFactory)
|
||||
func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol, timelineItemFactory: RoomTimelineItemFactoryProtocol, mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? {
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderCallsCount += 1
|
||||
buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedArguments = (roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider)
|
||||
DispatchQueue.main.async {
|
||||
self.buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReceivedInvocations.append((roomProxy: roomProxy, timelineItemFactory: timelineItemFactory))
|
||||
self.buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReceivedInvocations.append((roomProxy: roomProxy, timelineItemFactory: timelineItemFactory, mediaProvider: mediaProvider))
|
||||
}
|
||||
if let buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryClosure = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryClosure {
|
||||
return await buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryClosure(roomProxy, timelineItemFactory)
|
||||
if let buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure = buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure {
|
||||
return await buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderClosure(roomProxy, timelineItemFactory, mediaProvider)
|
||||
} else {
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryReturnValue
|
||||
return buildRoomPinnedTimelineControllerRoomProxyTimelineItemFactoryMediaProviderReturnValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ extension RoomTimelineControllerFactoryMock {
|
||||
convenience init(configuration: RoomTimelineControllerFactoryMockConfiguration) {
|
||||
self.init()
|
||||
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReturnValue = configuration.timelineController ?? {
|
||||
buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReturnValue = configuration.timelineController ?? {
|
||||
let timelineController = MockRoomTimelineController()
|
||||
timelineController.timelineItems = RoomTimelineItemFixtures.largeChunk
|
||||
return timelineController
|
||||
|
@ -6,29 +6,55 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
enum AvatarSize {
|
||||
case user(on: UserAvatarSizeOnScreen)
|
||||
case room(on: RoomAvatarSizeOnScreen)
|
||||
// custom
|
||||
case custom(CGFloat)
|
||||
enum Avatars {
|
||||
enum Size {
|
||||
case user(on: UserAvatarSizeOnScreen)
|
||||
case room(on: RoomAvatarSizeOnScreen)
|
||||
// custom
|
||||
case custom(CGFloat)
|
||||
|
||||
/// Value in UIKit points
|
||||
var value: CGFloat {
|
||||
switch self {
|
||||
case .user(let screen):
|
||||
return screen.value
|
||||
case .room(let screen):
|
||||
return screen.value
|
||||
case .custom(let val):
|
||||
return val
|
||||
/// Value in UIKit points
|
||||
var value: CGFloat {
|
||||
switch self {
|
||||
case .user(let screen):
|
||||
return screen.value
|
||||
case .room(let screen):
|
||||
return screen.value
|
||||
case .custom(let 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
|
||||
var scaledValue: CGFloat {
|
||||
value * UIScreen.main.scale
|
||||
|
||||
@MainActor
|
||||
static func generatePlaceholderAvatarImageData(name: String, id: String, size: CGSize) -> Data? {
|
||||
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 messageForwarding
|
||||
case globalSearch
|
||||
case roomSelection
|
||||
case details
|
||||
case notificationSettings
|
||||
case roomDirectorySearch
|
||||
@ -104,6 +131,8 @@ enum RoomAvatarSizeOnScreen {
|
||||
return 36
|
||||
case .globalSearch:
|
||||
return 36
|
||||
case .roomSelection:
|
||||
return 36
|
||||
case .home:
|
||||
return 52
|
||||
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
|
||||
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)
|
||||
|
||||
|
@ -183,37 +183,17 @@ extension UNMutableNotificationContent {
|
||||
@MainActor
|
||||
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
|
||||
let shouldFlipAvatar = shouldFlipAvatar()
|
||||
let prefix = "notification_placeholder\(shouldFlipAvatar ? "V9F" : "V9")"
|
||||
let prefix = "notification_placeholderV9"
|
||||
|
||||
let fileName = "\(prefix)_\(name)_\(id).png"
|
||||
if let data = try? Data(contentsOf: URL.temporaryDirectory.appendingPathComponent(fileName)) {
|
||||
MXLog.info("Found existing notification icon placeholder")
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
// 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()
|
||||
}
|
||||
let data = Avatars.generatePlaceholderAvatarImageData(name: name, id: id, size: .init(width: 50, height: 50))
|
||||
|
||||
if let data {
|
||||
do {
|
||||
@ -224,44 +204,7 @@ extension UNMutableNotificationContent {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ extension URL: @retroactive ExpressibleByStringLiteral {
|
||||
}
|
||||
|
||||
/// The base directory where all application support data is stored.
|
||||
static var cachesBaseDirectory: URL {
|
||||
static var sessionCachesBaseDirectory: URL {
|
||||
let url = appGroupContainerDirectory
|
||||
.appendingPathComponent("Library", isDirectory: true)
|
||||
.appendingPathComponent("Caches", isDirectory: true)
|
||||
@ -69,7 +69,20 @@ extension URL: @retroactive ExpressibleByStringLiteral {
|
||||
|
||||
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
|
||||
|
||||
return url
|
||||
|
@ -8,7 +8,7 @@
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum NSELogger {
|
||||
enum ExtensionLogger {
|
||||
private static var isConfigured = false
|
||||
|
||||
/// Memory formatter, uses exact 2 fraction digits and no grouping
|
||||
@ -66,13 +66,13 @@ enum NSELogger {
|
||||
return "\(formattedStr) MB"
|
||||
}
|
||||
|
||||
static func configure(logLevel: TracingConfiguration.LogLevel) {
|
||||
static func configure(currentTarget: String, logLevel: TracingConfiguration.LogLevel) {
|
||||
guard !isConfigured else {
|
||||
return
|
||||
}
|
||||
isConfigured = true
|
||||
|
||||
MXLog.configure(currentTarget: "nse", filePrefix: "nse", logLevel: logLevel)
|
||||
MXLog.configure(currentTarget: currentTarget, filePrefix: currentTarget, logLevel: logLevel)
|
||||
}
|
||||
|
||||
static func logMemory(with tag: String) {
|
@ -25,13 +25,13 @@ struct AvatarHeaderView<Footer: View>: View {
|
||||
private let subtitle: String?
|
||||
private let badges: [Badge]
|
||||
|
||||
private let avatarSize: AvatarSize
|
||||
private let avatarSize: Avatars.Size
|
||||
private let mediaProvider: MediaProviderProtocol?
|
||||
private var onAvatarTap: ((URL) -> Void)?
|
||||
@ViewBuilder private var footer: () -> Footer
|
||||
|
||||
init(room: RoomDetails,
|
||||
avatarSize: AvatarSize,
|
||||
avatarSize: Avatars.Size,
|
||||
mediaProvider: MediaProviderProtocol? = nil,
|
||||
onAvatarTap: ((URL) -> Void)? = nil,
|
||||
@ViewBuilder footer: @escaping () -> Footer) {
|
||||
@ -72,7 +72,7 @@ struct AvatarHeaderView<Footer: View>: View {
|
||||
|
||||
init(member: RoomMemberDetails,
|
||||
isVerified: Bool = false,
|
||||
avatarSize: AvatarSize,
|
||||
avatarSize: Avatars.Size,
|
||||
mediaProvider: MediaProviderProtocol? = nil,
|
||||
onAvatarTap: ((URL) -> Void)? = nil,
|
||||
@ViewBuilder footer: @escaping () -> Footer) {
|
||||
@ -88,7 +88,7 @@ struct AvatarHeaderView<Footer: View>: View {
|
||||
|
||||
init(user: UserProfileProxy,
|
||||
isVerified: Bool,
|
||||
avatarSize: AvatarSize,
|
||||
avatarSize: Avatars.Size,
|
||||
mediaProvider: MediaProviderProtocol? = nil,
|
||||
onAvatarTap: ((URL) -> Void)? = nil,
|
||||
@ViewBuilder footer: @escaping () -> Footer) {
|
||||
|
@ -11,7 +11,7 @@ struct LoadableAvatarImage: View {
|
||||
private let url: URL?
|
||||
private let name: String?
|
||||
private let contentID: String?
|
||||
private let avatarSize: AvatarSize
|
||||
private let avatarSize: Avatars.Size
|
||||
private let mediaProvider: MediaProviderProtocol?
|
||||
private let onTap: ((URL) -> Void)?
|
||||
|
||||
@ -19,7 +19,7 @@ struct LoadableAvatarImage: View {
|
||||
|
||||
init(url: URL?, name: String?,
|
||||
contentID: String?,
|
||||
avatarSize: AvatarSize,
|
||||
avatarSize: Avatars.Size,
|
||||
mediaProvider: MediaProviderProtocol?,
|
||||
onTap: ((URL) -> Void)? = nil) {
|
||||
self.url = url
|
||||
|
@ -12,12 +12,12 @@ struct OverridableAvatarImage: View {
|
||||
private let url: URL?
|
||||
private let name: String?
|
||||
private let contentID: String?
|
||||
private let avatarSize: AvatarSize
|
||||
private let avatarSize: Avatars.Size
|
||||
private let mediaProvider: MediaProviderProtocol?
|
||||
|
||||
@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.url = url
|
||||
self.name = name
|
||||
|
@ -22,7 +22,7 @@ enum RoomAvatar: Equatable {
|
||||
struct RoomAvatarImage: View {
|
||||
let avatar: RoomAvatar
|
||||
|
||||
let avatarSize: AvatarSize
|
||||
let avatarSize: Avatars.Size
|
||||
let mediaProvider: MediaProviderProtocol?
|
||||
|
||||
private(set) var onAvatarTap: ((URL) -> Void)?
|
||||
|
@ -18,7 +18,7 @@ struct StackedAvatarsView: View {
|
||||
let lineWidth: CGFloat
|
||||
var shouldStackFromLast = false
|
||||
let avatars: [StackedAvatarInfo]
|
||||
let avatarSize: AvatarSize
|
||||
let avatarSize: Avatars.Size
|
||||
let mediaProvider: MediaProviderProtocol?
|
||||
|
||||
var body: some View {
|
||||
|
@ -30,7 +30,7 @@ enum GlobalSearchScreenViewAction {
|
||||
|
||||
struct GlobalSearchRoom: Identifiable, Equatable {
|
||||
let id: String
|
||||
let name: String
|
||||
let alias: String?
|
||||
let title: String
|
||||
let description: String
|
||||
let avatar: RoomAvatar
|
||||
}
|
||||
|
@ -36,7 +36,11 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
|
||||
.map(\.bindings.searchQuery)
|
||||
.removeDuplicates()
|
||||
.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)
|
||||
|
||||
@ -66,8 +70,8 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
|
||||
private func updateRooms(with summaries: [RoomSummary]) {
|
||||
state.rooms = summaries.compactMap { summary in
|
||||
GlobalSearchRoom(id: summary.id,
|
||||
name: summary.name,
|
||||
alias: summary.canonicalAlias,
|
||||
title: summary.name,
|
||||
description: summary.roomListDescription,
|
||||
avatar: summary.avatar)
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ struct GlobalSearchScreenListRow: View {
|
||||
|
||||
var body: some View {
|
||||
ZStack { // The list row swallows listRowBackgrounds for some reason
|
||||
ListRow(label: .avatar(title: room.name,
|
||||
description: room.alias ?? room.id,
|
||||
ListRow(label: .avatar(title: room.title,
|
||||
description: room.description,
|
||||
icon: avatar),
|
||||
kind: .label)
|
||||
}
|
||||
@ -42,8 +42,8 @@ struct GlobalSearchScreenListRow_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
List {
|
||||
GlobalSearchScreenListRow(room: .init(id: "123",
|
||||
name: "Tech central",
|
||||
alias: "The best place in the whole wide world",
|
||||
title: "Tech central",
|
||||
description: "The best place in the whole wide world",
|
||||
avatar: .room(id: "123",
|
||||
name: "Tech central",
|
||||
avatarURL: .picturesDirectory)),
|
||||
|
@ -33,8 +33,8 @@ enum MessageForwardingScreenViewAction {
|
||||
|
||||
struct MessageForwardingRoom: Identifiable, Equatable {
|
||||
let id: String
|
||||
let name: String
|
||||
let alias: String?
|
||||
let title: String
|
||||
let description: String
|
||||
let avatar: RoomAvatar
|
||||
}
|
||||
|
||||
|
@ -45,8 +45,11 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
||||
.map(\.bindings.searchQuery)
|
||||
.removeDuplicates()
|
||||
.sink { [weak self] searchQuery in
|
||||
guard let self else { return }
|
||||
self.roomSummaryProvider.setFilter(.search(query: searchQuery))
|
||||
if searchQuery.isEmpty {
|
||||
self?.roomSummaryProvider.setFilter(.all(filters: []))
|
||||
} else {
|
||||
self?.roomSummaryProvider.setFilter(.search(query: searchQuery))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
@ -79,8 +82,10 @@ class MessageForwardingScreenViewModel: MessageForwardingScreenViewModelType, Me
|
||||
continue
|
||||
}
|
||||
|
||||
let room = MessageForwardingRoom(id: summary.id, name: summary.name, alias: summary.canonicalAlias, avatar: summary.avatar)
|
||||
rooms.append(room)
|
||||
rooms.append(.init(id: summary.id,
|
||||
title: summary.name,
|
||||
description: summary.roomListDescription,
|
||||
avatar: summary.avatar))
|
||||
}
|
||||
|
||||
state.rooms = rooms
|
||||
|
@ -68,8 +68,8 @@ private struct MessageForwardingListRow: View {
|
||||
let context: MessageForwardingScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
ListRow(label: .avatar(title: room.name,
|
||||
description: room.alias ?? room.id,
|
||||
ListRow(label: .avatar(title: room.title,
|
||||
description: room.description,
|
||||
icon: avatar),
|
||||
kind: .selection(isSelected: isSelected) {
|
||||
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,
|
||||
eventStringBuilder: eventStringBuilder,
|
||||
name: "MessageForwarding",
|
||||
name: "AlternateAllRooms",
|
||||
notificationSettings: notificationSettings,
|
||||
appSettings: appSettings)
|
||||
try await alternateRoomSummaryProvider?.setRoomList(roomListService.allRooms())
|
||||
|
@ -69,6 +69,20 @@ extension RoomSummary: CustomStringConvertible {
|
||||
- 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 {
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import IntentsUI
|
||||
import MatrixRustSDK
|
||||
import UIKit
|
||||
|
||||
@ -14,7 +15,9 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
private let roomProxy: JoinedRoomProxyProtocol
|
||||
private let liveTimelineProvider: RoomTimelineProviderProtocol
|
||||
private let timelineItemFactory: RoomTimelineItemFactoryProtocol
|
||||
private let mediaProvider: MediaProviderProtocol
|
||||
private let appSettings: AppSettings
|
||||
|
||||
private let serialDispatchQueue: DispatchQueue
|
||||
|
||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||
@ -40,11 +43,14 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
timelineProxy: TimelineProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
appSettings: AppSettings) {
|
||||
self.roomProxy = roomProxy
|
||||
liveTimelineProvider = timelineProxy.timelineProvider
|
||||
self.timelineItemFactory = timelineItemFactory
|
||||
self.mediaProvider = mediaProvider
|
||||
self.appSettings = appSettings
|
||||
|
||||
serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider", qos: .utility)
|
||||
|
||||
activeTimeline = timelineProxy
|
||||
@ -153,11 +159,63 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||
intentionalMentions: intentionalMentions) {
|
||||
case .success:
|
||||
MXLog.info("Finished sending message")
|
||||
await donateSendMessageIntent()
|
||||
case .failure(let 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 {
|
||||
MXLog.info("Toggle reaction \(reaction) to \(eventOrTransactionID)")
|
||||
|
||||
|
@ -10,16 +10,19 @@ import Foundation
|
||||
struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
|
||||
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol) -> RoomTimelineControllerProtocol {
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol {
|
||||
RoomTimelineController(roomProxy: roomProxy,
|
||||
timelineProxy: roomProxy.timeline,
|
||||
initialFocussedEventID: initialFocussedEventID,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
|
||||
func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol) async -> RoomTimelineControllerProtocol? {
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol? {
|
||||
guard let pinnedEventsTimeline = await roomProxy.pinnedEventsTimeline else {
|
||||
return nil
|
||||
}
|
||||
@ -27,6 +30,7 @@ struct RoomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol {
|
||||
timelineProxy: pinnedEventsTimeline,
|
||||
initialFocussedEventID: nil,
|
||||
timelineItemFactory: timelineItemFactory,
|
||||
mediaProvider: mediaProvider,
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,11 @@ import Foundation
|
||||
protocol RoomTimelineControllerFactoryProtocol {
|
||||
func buildRoomTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
initialFocussedEventID: String?,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol) -> RoomTimelineControllerProtocol
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) -> RoomTimelineControllerProtocol
|
||||
func buildRoomPinnedTimelineController(roomProxy: JoinedRoomProxyProtocol,
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol) async -> RoomTimelineControllerProtocol?
|
||||
timelineItemFactory: RoomTimelineItemFactoryProtocol,
|
||||
mediaProvider: MediaProviderProtocol) async -> RoomTimelineControllerProtocol?
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
|
@ -72,19 +72,19 @@ extension SessionDirectories {
|
||||
init() {
|
||||
let sessionDirectoryName = UUID().uuidString
|
||||
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.
|
||||
init(userID: String) {
|
||||
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.
|
||||
init(dataDirectory: URL) {
|
||||
self.dataDirectory = dataDirectory
|
||||
cacheDirectory = .cachesBaseDirectory.appending(component: dataDirectory.lastPathComponent)
|
||||
cacheDirectory = .sessionCachesBaseDirectory.appending(component: dataDirectory.lastPathComponent)
|
||||
}
|
||||
}
|
||||
|
||||
|
21
ElementX/Sources/ShareExtension/ShareExtensionModels.swift
Normal file
21
ElementX/Sources/ShareExtension/ShareExtensionModels.swift
Normal file
@ -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",
|
||||
attributedStringBuilder: AttributedStringBuilder(mentionBuilder: MentionBuilder()),
|
||||
stateEventStringBuilder: RoomStateEventStringBuilder(userID: "@alice:matrix.org")),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()),
|
||||
appSettings: ServiceLocator.shared.settings)
|
||||
|
||||
let flowCoordinator = UserSessionFlowCoordinator(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
||||
|
@ -54,7 +54,7 @@
|
||||
<string>Application</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>io.element</string>
|
||||
<string>$(BASE_BUNDLE_IDENTIFIER)</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
@ -66,7 +66,7 @@ targets:
|
||||
CFBundleTypeRole: Editor,
|
||||
CFBundleURLName: "Application",
|
||||
CFBundleURLSchemes: [
|
||||
io.element
|
||||
$(BASE_BUNDLE_IDENTIFIER)
|
||||
]
|
||||
}
|
||||
]
|
||||
@ -189,6 +189,7 @@ targets:
|
||||
|
||||
dependencies:
|
||||
- target: NSE
|
||||
- target: ShareExtension
|
||||
# not used yet
|
||||
# - target: NCE
|
||||
- package: MatrixRustSDK
|
||||
|
@ -66,10 +66,10 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
handler = contentHandler
|
||||
modifiedContent = request.content.mutableCopy() as? UNMutableNotificationContent
|
||||
|
||||
NSELogger.configure(logLevel: settings.logLevel)
|
||||
ExtensionLogger.configure(currentTarget: "nse", logLevel: settings.logLevel)
|
||||
|
||||
MXLog.info("\(tag) #########################################")
|
||||
NSELogger.logMemory(with: tag)
|
||||
ExtensionLogger.logMemory(with: tag)
|
||||
MXLog.info("\(tag) Payload came: \(request.content.userInfo)")
|
||||
|
||||
Self.serialQueue.sync {
|
||||
@ -201,7 +201,7 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
||||
|
||||
deinit {
|
||||
cleanUp()
|
||||
NSELogger.logMemory(with: tag)
|
||||
ExtensionLogger.logMemory(with: tag)
|
||||
MXLog.info("\(tag) deinit")
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ targets:
|
||||
- path: ../../ElementX/Sources/Application/AppSettings.swift
|
||||
- path: ../../ElementX/Sources/Generated/Assets.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/Extensions/AttributedString.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() {
|
||||
for preview in SFNumberedListView_Previews._allPreviews {
|
||||
assertSnapshots(matching: preview)
|
||||
|
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_globalSearchScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPad-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPad-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_messageForwardingScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPad-en-GB.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPad-pseudo.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPhone-16-en-GB.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Normal file
BIN
PreviewTests/Sources/__Snapshots__/PreviewTests/test_roomSelectionScreen-iPhone-16-pseudo.1.png
(Stored with Git LFS)
Normal file
Binary file not shown.
141
ShareExtension/Sources/ShareExtensionViewController.swift
Normal file
141
ShareExtension/Sources/ShareExtensionViewController.swift
Normal file
@ -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
|
||||
}
|
||||
}
|
||||
}
|
23
ShareExtension/Sources/View/ShareExtensionView.swift
Normal file
23
ShareExtension/Sources/View/ShareExtensionView.swift
Normal file
@ -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
ShareExtension/SupportingFiles/Info.plist
Normal file
55
ShareExtension/SupportingFiles/Info.plist
Normal 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>
|
10
ShareExtension/SupportingFiles/ShareExtension.entitlements
Normal file
10
ShareExtension/SupportingFiles/ShareExtension.entitlements
Normal file
@ -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
ShareExtension/SupportingFiles/target.yml
Normal file
85
ShareExtension/SupportingFiles/target.yml
Normal 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
func test_whenImageFromSourceWithSourceNotNilAndImageCacheContainsImage_ImageIsReturned() throws {
|
||||
let avatarSize = AvatarSize.room(on: .timeline)
|
||||
let avatarSize = Avatars.Size.room(on: .timeline)
|
||||
let url = URL.picturesDirectory
|
||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||
let imageForKey = UIImage()
|
||||
@ -90,12 +90,12 @@ final class MediaProviderTests: XCTestCase {
|
||||
|
||||
func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws {
|
||||
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)
|
||||
}
|
||||
|
||||
func test_whenLoadImageFromSourceAndImageCacheContainsImage_successIsReturned() async throws {
|
||||
let avatarSize = AvatarSize.room(on: .timeline)
|
||||
let avatarSize = Avatars.Size.room(on: .timeline)
|
||||
let url = URL.picturesDirectory
|
||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||
let imageForKey = UIImage()
|
||||
@ -106,7 +106,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageSucceeds_successIsReturned() async throws {
|
||||
let avatarSize = AvatarSize.room(on: .timeline)
|
||||
let avatarSize = Avatars.Size.room(on: .timeline)
|
||||
let url = URL.picturesDirectory
|
||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||
let imageForKey = UIImage()
|
||||
@ -117,7 +117,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageThumbnailIsLoaded() async throws {
|
||||
let avatarSize = AvatarSize.room(on: .timeline)
|
||||
let avatarSize = Avatars.Size.room(on: .timeline)
|
||||
let expectedImage = try loadTestImage()
|
||||
|
||||
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = expectedImage.pngData()
|
||||
@ -133,7 +133,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
}
|
||||
|
||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageIsStored() async throws {
|
||||
let avatarSize = AvatarSize.room(on: .timeline)
|
||||
let avatarSize = Avatars.Size.room(on: .timeline)
|
||||
let url = URL.picturesDirectory
|
||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||
let expectedImage = try loadTestImage()
|
||||
@ -165,7 +165,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
mediaLoader.loadMediaThumbnailForSourceWidthHeightThrowableError = MediaProviderTestsError.error
|
||||
|
||||
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 {
|
||||
case .success:
|
||||
XCTFail("Should fail")
|
||||
@ -191,7 +191,7 @@ final class MediaProviderTests: XCTestCase {
|
||||
mediaLoader.loadMediaThumbnailForSourceWidthHeightReturnValue = Data()
|
||||
|
||||
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 {
|
||||
case .success:
|
||||
XCTFail("Should fail")
|
||||
|
@ -31,7 +31,7 @@ class RestorationTokenTests: XCTestCase {
|
||||
XCTAssertNil(decodedToken.pusherNotificationClientIdentifier, "There should not be a push notification client ID.")
|
||||
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.")
|
||||
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.")
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ class RestorationTokenTests: XCTestCase {
|
||||
"The push notification client identifier should not be changed.")
|
||||
XCTAssertEqual(decodedToken.sessionDirectories.dataDirectory, originalToken.sessionDirectory,
|
||||
"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.")
|
||||
}
|
||||
|
||||
@ -75,7 +75,7 @@ class RestorationTokenTests: XCTestCase {
|
||||
oidcData: "data-from-mas",
|
||||
slidingSyncVersion: .native),
|
||||
sessionDirectory: .sessionsBaseDirectory.appending(component: sessionDirectoryName),
|
||||
cacheDirectory: .cachesBaseDirectory.appending(component: sessionDirectoryName),
|
||||
cacheDirectory: .sessionCachesBaseDirectory.appending(component: sessionDirectoryName),
|
||||
passphrase: "passphrase",
|
||||
pusherNotificationClientIdentifier: "pusher-identifier")
|
||||
let data = try JSONEncoder().encode(originalToken)
|
||||
|
@ -218,6 +218,31 @@ class RoomFlowCoordinatorTests: XCTestCase {
|
||||
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
|
||||
|
||||
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
|
||||
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() {
|
||||
@ -34,7 +34,7 @@ class SessionDirectoriesTests: XCTestCase {
|
||||
|
||||
// Then the data directory should remain unchanged and the caches directory should be generated.
|
||||
XCTAssertEqual(sessionDirectories.dataDirectory, sessionDirectory)
|
||||
XCTAssertEqual(sessionDirectories.cacheDirectory, .cachesBaseDirectory.appending(component: sessionDirectoryName))
|
||||
XCTAssertEqual(sessionDirectories.cacheDirectory, .sessionCachesBaseDirectory.appending(component: sessionDirectoryName))
|
||||
}
|
||||
|
||||
func testPathOutput() {
|
||||
|
@ -211,8 +211,8 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 1)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "1")
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 1)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderReceivedArguments?.initialFocussedEventID, "1")
|
||||
|
||||
// A child event route should push a new room screen onto the stack and focus on the event.
|
||||
userSessionFlowCoordinator.handleAppRoute(.childEvent(eventID: "2", roomID: "2", via: []), animated: true)
|
||||
@ -221,27 +221,50 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 1)
|
||||
XCTAssertTrue(detailNavigationStack?.stackCoordinators.first is RoomScreenCoordinator)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 2)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "2")
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 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.
|
||||
try await process(route: .event(eventID: "3", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 3)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "3")
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 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.
|
||||
try await process(route: .event(eventID: "4", roomID: "3", via: []), expectedState: .roomList(selectedRoomID: "3"))
|
||||
XCTAssertTrue(detailNavigationStack?.rootCoordinator is RoomScreenCoordinator)
|
||||
XCTAssertEqual(detailNavigationStack?.stackCoordinators.count, 0)
|
||||
XCTAssertNotNil(detailCoordinator)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryCallsCount, 4)
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryReceivedArguments?.initialFocussedEventID, "4",
|
||||
XCTAssertEqual(timelineControllerFactory.buildRoomTimelineControllerRoomProxyInitialFocussedEventIDTimelineItemFactoryMediaProviderCallsCount, 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.")
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
private func process(route: AppRoute, expectedState: UserSessionFlowCoordinatorStateMachine.State) async throws {
|
||||
|
@ -53,6 +53,7 @@ include:
|
||||
- path: UITests/SupportingFiles/target.yml
|
||||
- path: IntegrationTests/SupportingFiles/target.yml
|
||||
- path: NSE/SupportingFiles/target.yml
|
||||
- path: ShareExtension/SupportingFiles/target.yml
|
||||
# - path: NCE/SupportingFiles/target.yml (not used yet)
|
||||
# - path: MyAppVariant/override.yml
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user