mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 11:58:32 +00:00
Media loading flow changes (#483)
* Use an imageProvider directly from the view in the home screen * Add support for media request coalescing * Rename MediaProxy to MediaLoader * Add new image loading mechanism to the room details screen avatar. * Use the `SettingsScreen` prefix for all settings screen related components * Add new image loading mechanism to the room header * Add new image loading mechanism to the room member details screen * Introduce a LoadableImage SwiftUI view that will automatically handle image loading * Adopt the new LoadableImage where possible * Fix LoadableImage not using/storing loaded images properly * Simplify media loader enqueueing * Made LodableImage load content after mediaSource updates. Adopt it on the home and settings screens * Introduce a LoadableAvatarImage component and reuse it throughout the app * Small logging tweaks, made some LoadableImage properties private * Fix redacted skeletons avatar background color * Fix placeholder avatars changing when backgrounding the app * PR comments. - Trim the @ sign off of mxid placeholders. - Only expose AvatarSize on the avatar image, use CGSize elsewhere. Co-authored-by: Doug <douglase@element.io>
This commit is contained in:
@@ -74,12 +74,15 @@
|
|||||||
1E59B77A0B2CE83DCC1B203C /* LoginViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A05707BF550D770168A406DB /* LoginViewModelTests.swift */; };
|
1E59B77A0B2CE83DCC1B203C /* LoginViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A05707BF550D770168A406DB /* LoginViewModelTests.swift */; };
|
||||||
1F04C63D4FA95948E3F52147 /* FileRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51E3D86A84341C3A0CB8A40 /* FileRoomTimelineView.swift */; };
|
1F04C63D4FA95948E3F52147 /* FileRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E51E3D86A84341C3A0CB8A40 /* FileRoomTimelineView.swift */; };
|
||||||
1F3232BD368DF430AB433907 /* DesignKit in Frameworks */ = {isa = PBXBuildFile; productRef = A5A56C4F47C368EBE5C5E870 /* DesignKit */; };
|
1F3232BD368DF430AB433907 /* DesignKit in Frameworks */ = {isa = PBXBuildFile; productRef = A5A56C4F47C368EBE5C5E870 /* DesignKit */; };
|
||||||
|
1FD56B9EA4CA804120A2E743 /* ImageProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D28E3A5AE9934EE4FB4720 /* ImageProviderProtocol.swift */; };
|
||||||
1FE593ECEC40A43789105D80 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */; };
|
1FE593ECEC40A43789105D80 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */; };
|
||||||
1FEC0A4EC6E6DF693C16B32A /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEBCB9676FCD1D0F13188DD /* StringTests.swift */; };
|
1FEC0A4EC6E6DF693C16B32A /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CEBCB9676FCD1D0F13188DD /* StringTests.swift */; };
|
||||||
206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; };
|
206F0DBAB6AF042CA1FF2C0D /* SettingsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */; };
|
||||||
|
20840B4549FFF1301D0A5FF2 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A46F50F2A46103ADF143ADB /* MediaLoader.swift */; };
|
||||||
214C6B416609E58CCBF6DCEE /* SoftLogoutModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC77FC5C4F2000133047AA27 /* SoftLogoutModels.swift */; };
|
214C6B416609E58CCBF6DCEE /* SoftLogoutModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC77FC5C4F2000133047AA27 /* SoftLogoutModels.swift */; };
|
||||||
214CDBF0C783155242FFE4A0 /* NotificationItemProxy+NSE.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B1FBF8CA40199B8058B1F08 /* NotificationItemProxy+NSE.swift */; };
|
214CDBF0C783155242FFE4A0 /* NotificationItemProxy+NSE.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B1FBF8CA40199B8058B1F08 /* NotificationItemProxy+NSE.swift */; };
|
||||||
2276870A19F34B3FFFDA690F /* SoftLogoutCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEA20A6B4883E60469ACF8F /* SoftLogoutCoordinator.swift */; };
|
2276870A19F34B3FFFDA690F /* SoftLogoutCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AEA20A6B4883E60469ACF8F /* SoftLogoutCoordinator.swift */; };
|
||||||
|
233221E32DA045018D3D3050 /* SettingsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FF540C393C7DDEE9C902DFF /* SettingsScreenCoordinator.swift */; };
|
||||||
2352C541AF857241489756FF /* MockRoomSummaryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */; };
|
2352C541AF857241489756FF /* MockRoomSummaryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */; };
|
||||||
237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; };
|
237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; };
|
||||||
23B2CD5A06B16055BDDD0994 /* ApplicationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44D8C8431416EB8DFEC7E235 /* ApplicationTests.swift */; };
|
23B2CD5A06B16055BDDD0994 /* ApplicationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44D8C8431416EB8DFEC7E235 /* ApplicationTests.swift */; };
|
||||||
@@ -100,7 +103,6 @@
|
|||||||
2BA59D0AEFB4B82A2EC2A326 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; };
|
2BA59D0AEFB4B82A2EC2A326 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 78A5A8DE1E2B09C978C7F3B0 /* KeychainAccess */; };
|
||||||
2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */; };
|
2BAA5B222856068158D0B3C6 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */; };
|
||||||
2C5E832434EE94E21AB3B238 /* EmojiPickerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EAE3E9D5EF4A6D5D9C6CFD /* EmojiPickerScreenViewModel.swift */; };
|
2C5E832434EE94E21AB3B238 /* EmojiPickerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3EAE3E9D5EF4A6D5D9C6CFD /* EmojiPickerScreenViewModel.swift */; };
|
||||||
2CA8AD07773A38BA4662098B /* MediaProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC3D31C2DA6910AA0079678A /* MediaProxyProtocol.swift */; };
|
|
||||||
2CB6787E25B11711518E9588 /* OnboardingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6281B199D8A8F0892490C2E /* OnboardingCoordinator.swift */; };
|
2CB6787E25B11711518E9588 /* OnboardingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6281B199D8A8F0892490C2E /* OnboardingCoordinator.swift */; };
|
||||||
2D794361CFE790C8FB3C9C0F /* message.caf in Resources */ = {isa = PBXBuildFile; fileRef = ED482057AE39D5C6D9C5F3D8 /* message.caf */; };
|
2D794361CFE790C8FB3C9C0F /* message.caf in Resources */ = {isa = PBXBuildFile; fileRef = ED482057AE39D5C6D9C5F3D8 /* message.caf */; };
|
||||||
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */; };
|
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */; };
|
||||||
@@ -109,12 +111,10 @@
|
|||||||
2F94054F50E312AF30BE07F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B21E611DADDEF00307E7AC /* String.swift */; };
|
2F94054F50E312AF30BE07F3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40B21E611DADDEF00307E7AC /* String.swift */; };
|
||||||
308BD9343B95657FAA583FB7 /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = AD2AC190E55B2BD4D0F1D4A7 /* SwiftyBeaver */; };
|
308BD9343B95657FAA583FB7 /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = AD2AC190E55B2BD4D0F1D4A7 /* SwiftyBeaver */; };
|
||||||
3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */; };
|
3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */; };
|
||||||
323F36D880363C473B81A9EA /* MediaProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC3D31C2DA6910AA0079678A /* MediaProxyProtocol.swift */; };
|
|
||||||
3274219F7F26A5C6C2C55630 /* FilePreviewViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F652E88106B855A2A55ADE /* FilePreviewViewModelProtocol.swift */; };
|
3274219F7F26A5C6C2C55630 /* FilePreviewViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F652E88106B855A2A55ADE /* FilePreviewViewModelProtocol.swift */; };
|
||||||
33CAC1226DFB8B5D8447D286 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 3853B78FB8531B83936C5DA6 /* SwiftState */; };
|
33CAC1226DFB8B5D8447D286 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 3853B78FB8531B83936C5DA6 /* SwiftState */; };
|
||||||
33D630461FC4562CC767EE9F /* FileCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B0B1226DA8DB55918B34CD /* FileCache.swift */; };
|
33D630461FC4562CC767EE9F /* FileCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B0B1226DA8DB55918B34CD /* FileCache.swift */; };
|
||||||
340D39DB87F3800D53A6A621 /* EmojiPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00245D40CD90FD71D6A05239 /* EmojiPickerScreen.swift */; };
|
340D39DB87F3800D53A6A621 /* EmojiPickerScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00245D40CD90FD71D6A05239 /* EmojiPickerScreen.swift */; };
|
||||||
34966D4C1C2C6D37FE3F7F50 /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DD2D50A7EAA4FC78417730E /* SettingsCoordinator.swift */; };
|
|
||||||
34C752A73717C691582DC6C7 /* UnsupportedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */; };
|
34C752A73717C691582DC6C7 /* UnsupportedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */; };
|
||||||
352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */; };
|
352C439BE0F75E101EF11FB1 /* RoomScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */; };
|
||||||
3588F34D05B4D731A73214C6 /* BugReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED59F9EFF273BFA2055FFDF /* BugReportScreen.swift */; };
|
3588F34D05B4D731A73214C6 /* BugReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED59F9EFF273BFA2055FFDF /* BugReportScreen.swift */; };
|
||||||
@@ -129,7 +129,6 @@
|
|||||||
39929D29B265C3F6606047DE /* AlignedScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8872E9C5E91E9F2BFC4EBCCA /* AlignedScrollView.swift */; };
|
39929D29B265C3F6606047DE /* AlignedScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8872E9C5E91E9F2BFC4EBCCA /* AlignedScrollView.swift */; };
|
||||||
3A08584ECDD4A4541DBF21F8 /* EmojiLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */; };
|
3A08584ECDD4A4541DBF21F8 /* EmojiLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */; };
|
||||||
3A64A93A651A3CB8774ADE8E /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; };
|
3A64A93A651A3CB8774ADE8E /* SnapshotTesting in Frameworks */ = {isa = PBXBuildFile; productRef = 21C83087604B154AA30E9A8F /* SnapshotTesting */; };
|
||||||
3B770CB4DED51CC362C66D47 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4990FDBDA96B88E214F92F48 /* SettingsModels.swift */; };
|
|
||||||
3C549A0BF39F8A854D45D9FD /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; };
|
3C549A0BF39F8A854D45D9FD /* PostHog in Frameworks */ = {isa = PBXBuildFile; productRef = 4278261E147DB2DE5CFB7FC5 /* PostHog */; };
|
||||||
3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */; };
|
3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */; };
|
||||||
3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC38E64A5ED3FDB201029A /* BugReportService.swift */; };
|
3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC38E64A5ED3FDB201029A /* BugReportService.swift */; };
|
||||||
@@ -142,18 +141,16 @@
|
|||||||
41DFDD212D1BE57CA50D783B /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = FD43A50D9B75C9D6D30F006B /* SwiftyBeaver */; };
|
41DFDD212D1BE57CA50D783B /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = FD43A50D9B75C9D6D30F006B /* SwiftyBeaver */; };
|
||||||
4219391CD2351E410554B3E8 /* AggregratedReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */; };
|
4219391CD2351E410554B3E8 /* AggregratedReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B858A61F2A570DFB8DE570A7 /* AggregratedReaction.swift */; };
|
||||||
42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A307A44F952CD73E63AE31 /* RoomEventStringBuilder.swift */; };
|
42F1C8731166633E35A6D7E6 /* RoomEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A307A44F952CD73E63AE31 /* RoomEventStringBuilder.swift */; };
|
||||||
438FB9BC535BC95948AA5F34 /* SettingsViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B2F9D5C39A4494D19F33E38 /* SettingsViewModelProtocol.swift */; };
|
|
||||||
43BD17BC8794BB9B04F2A26B /* MediaSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179423E34EE846E048E49CBF /* MediaSourceProxy.swift */; };
|
43BD17BC8794BB9B04F2A26B /* MediaSourceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179423E34EE846E048E49CBF /* MediaSourceProxy.swift */; };
|
||||||
43FD77998F33C32718C51450 /* TemplateCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD460ED7ED1E03B85DEA25C /* TemplateCoordinator.swift */; };
|
43FD77998F33C32718C51450 /* TemplateCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBD460ED7ED1E03B85DEA25C /* TemplateCoordinator.swift */; };
|
||||||
440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */; };
|
440123E29E2F9B001A775BBE /* TimelineItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D505843AB66822EB91F0DF0 /* TimelineItemProxy.swift */; };
|
||||||
447E8580A0A2569E32529E17 /* MockRoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6094DEAAEB388E1AE118C6 /* MockRoomTimelineProvider.swift */; };
|
447E8580A0A2569E32529E17 /* MockRoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D6094DEAAEB388E1AE118C6 /* MockRoomTimelineProvider.swift */; };
|
||||||
44AE0752E001D1D10605CD88 /* Swipe.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FDA5344F7C4C6E4E863E13 /* Swipe.swift */; };
|
44AE0752E001D1D10605CD88 /* Swipe.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FDA5344F7C4C6E4E863E13 /* Swipe.swift */; };
|
||||||
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; };
|
46562110EE202E580A5FFD9C /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93CF7B19FFCF8EFBE0A8696A /* RoomScreenViewModelTests.swift */; };
|
||||||
490E606044B18985055FF690 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3E29F98CF0E960689A410E3 /* SettingsUITests.swift */; };
|
|
||||||
492274DA6691EE985C2FCCAA /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; };
|
492274DA6691EE985C2FCCAA /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 1BCD21310B997A6837B854D6 /* GZIP */; };
|
||||||
49E9B99CB6A275C7744351F0 /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D58333B377888012740101 /* LoginViewModel.swift */; };
|
49E9B99CB6A275C7744351F0 /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F2D58333B377888012740101 /* LoginViewModel.swift */; };
|
||||||
49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */; };
|
49F2E7DD8CAACE09CEECE3E6 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */; };
|
||||||
4A2E0DBB63919AC8309B6D40 /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A191D3FDB995309C7E2DE7D /* SettingsViewModel.swift */; };
|
4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */; };
|
||||||
4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C698E30698EC59302A8EEBD /* NavigationStackCoordinatorTests.swift */; };
|
4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C698E30698EC59302A8EEBD /* NavigationStackCoordinatorTests.swift */; };
|
||||||
4C3365818DE1CEAEDF590FD3 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */; };
|
4C3365818DE1CEAEDF590FD3 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */; };
|
||||||
4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */; };
|
4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */; };
|
||||||
@@ -163,6 +160,7 @@
|
|||||||
4FF90E2242DBD596E1ED2E27 /* AppCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */; };
|
4FF90E2242DBD596E1ED2E27 /* AppCoordinatorStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */; };
|
||||||
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874A1842477895F199567BD7 /* TimelineView.swift */; };
|
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 874A1842477895F199567BD7 /* TimelineView.swift */; };
|
||||||
501304F26B52DF7024011B6C /* EmojiMartJSONLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF9E3E6A23180EC05F06460 /* EmojiMartJSONLoaderTests.swift */; };
|
501304F26B52DF7024011B6C /* EmojiMartJSONLoaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BF9E3E6A23180EC05F06460 /* EmojiMartJSONLoaderTests.swift */; };
|
||||||
|
50C59870BEB1F29C60252FD4 /* SettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F72B9701D847C591ABE1A8 /* SettingsScreenViewModelProtocol.swift */; };
|
||||||
50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
50C90117FE25390BFBD40173 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
||||||
518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; };
|
518C93DC6516D3D018DE065F /* UNNotificationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */; };
|
||||||
51DB67C5B5BC68B0A6FF54D4 /* MockRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBDC1D28EFB7789EB467E0 /* MockRoomProxy.swift */; };
|
51DB67C5B5BC68B0A6FF54D4 /* MockRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACBDC1D28EFB7789EB467E0 /* MockRoomProxy.swift */; };
|
||||||
@@ -175,6 +173,7 @@
|
|||||||
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */; };
|
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24F5530B2212862FA4BEFF2D /* HomeScreenViewModelProtocol.swift */; };
|
||||||
588411C8FD72B2A2DFE5F7DE /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E992D7B8BE54B2AB454613AF /* XCUIElement.swift */; };
|
588411C8FD72B2A2DFE5F7DE /* XCUIElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = E992D7B8BE54B2AB454613AF /* XCUIElement.swift */; };
|
||||||
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2245243369B99216C7D84E /* ImageCache.swift */; };
|
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2245243369B99216C7D84E /* ImageCache.swift */; };
|
||||||
|
5B300BACD8A1B252AC95FA34 /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3DF4FAB9FBEF782DF08F3A /* MediaLoaderProtocol.swift */; };
|
||||||
5B8B51CEC4717AF487794685 /* NotificationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B490675B8E31423AF116BDA /* NotificationServiceProxy.swift */; };
|
5B8B51CEC4717AF487794685 /* NotificationServiceProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B490675B8E31423AF116BDA /* NotificationServiceProxy.swift */; };
|
||||||
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; };
|
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; };
|
||||||
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; };
|
5C8AFBF168A41E20835F3B86 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB34B0C74CD242FED9DD069 /* LoginScreenUITests.swift */; };
|
||||||
@@ -215,7 +214,9 @@
|
|||||||
6C9F6C7F2B35288C4230EF3F /* FilePreviewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55EA4B03F92F31EAA83B3F7B /* FilePreviewModels.swift */; };
|
6C9F6C7F2B35288C4230EF3F /* FilePreviewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55EA4B03F92F31EAA83B3F7B /* FilePreviewModels.swift */; };
|
||||||
6CA81428F0970785CDCC5E86 /* UserNotificationToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31A4E5941ACBA4BB9FEF94C /* UserNotificationToastView.swift */; };
|
6CA81428F0970785CDCC5E86 /* UserNotificationToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F31A4E5941ACBA4BB9FEF94C /* UserNotificationToastView.swift */; };
|
||||||
6D046D653DA28ADF1E6E59A4 /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */; };
|
6D046D653DA28ADF1E6E59A4 /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */; };
|
||||||
|
6E47D126DD7585E8F8237CE7 /* LoadableAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */; };
|
||||||
6E6E0AAF6C44C0B117EBBE5A /* SlidingSyncViewProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F3B445BD6EF1C751806B22 /* SlidingSyncViewProxy.swift */; };
|
6E6E0AAF6C44C0B117EBBE5A /* SlidingSyncViewProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 41F3B445BD6EF1C751806B22 /* SlidingSyncViewProxy.swift */; };
|
||||||
|
6EC7A40A537CFB3D526A111C /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47EBB5D698CE9A25BB553A2D /* Strings.swift */; };
|
||||||
6F2AB43A1EFAD8A97AF41A15 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = A7CA6F33C553805035C3B114 /* DeviceKit */; };
|
6F2AB43A1EFAD8A97AF41A15 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = A7CA6F33C553805035C3B114 /* DeviceKit */; };
|
||||||
6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */; };
|
6FC10A00D268FCD48B631E37 /* ViewFrameReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */; };
|
||||||
6FF51EB400DBA0668FC38B97 /* TimelineStartRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */; };
|
6FF51EB400DBA0668FC38B97 /* TimelineStartRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */; };
|
||||||
@@ -224,8 +225,10 @@
|
|||||||
70558528EF68CAAEF09972D5 /* RoomTimelineItemFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */; };
|
70558528EF68CAAEF09972D5 /* RoomTimelineItemFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */; };
|
||||||
706289B086B0A6B0C211763F /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; };
|
706289B086B0A6B0C211763F /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; };
|
||||||
706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */; };
|
706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */; };
|
||||||
|
709B1C6026A3056662FF93EE /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3DF4FAB9FBEF782DF08F3A /* MediaLoaderProtocol.swift */; };
|
||||||
719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; };
|
719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; };
|
||||||
71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */; };
|
71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */; };
|
||||||
|
7313970ED46B213AF4CFB4B3 /* MediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A46F50F2A46103ADF143ADB /* MediaLoader.swift */; };
|
||||||
7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 542D4F49FABA056DEEEB3400 /* RustTracing.swift */; };
|
||||||
7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */; };
|
7361B011A79BF723D8C9782B /* EmojiCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */; };
|
||||||
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0CBC76C80E04345E11F2DB /* Application.swift */; };
|
7405B4824D45BA7C3D943E76 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0CBC76C80E04345E11F2DB /* Application.swift */; };
|
||||||
@@ -249,7 +252,6 @@
|
|||||||
7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */; };
|
7BB31E67648CF32D2AB5E502 /* RoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CE3C90E487B255B735D73C8 /* RoomScreenViewModel.swift */; };
|
||||||
7C1A7B594B2F8143F0DD0005 /* ElementXAttributeScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C151639C4E1B91FCC68B /* ElementXAttributeScope.swift */; };
|
7C1A7B594B2F8143F0DD0005 /* ElementXAttributeScope.swift in Sources */ = {isa = PBXBuildFile; fileRef = C024C151639C4E1B91FCC68B /* ElementXAttributeScope.swift */; };
|
||||||
7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DFE4453AB0B34C203447162 /* ImageRoomTimelineItem.swift */; };
|
7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DFE4453AB0B34C203447162 /* ImageRoomTimelineItem.swift */; };
|
||||||
7D1DAAA364A9A29D554BD24E /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0950733DD4BA83EEE752E259 /* PlaceholderAvatarImage.swift */; };
|
|
||||||
7E3B1F8D72573ED2FCB2D94B /* NotificationServiceProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD5FEE195446A9E458DDDAF /* NotificationServiceProxyProtocol.swift */; };
|
7E3B1F8D72573ED2FCB2D94B /* NotificationServiceProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCD5FEE195446A9E458DDDAF /* NotificationServiceProxyProtocol.swift */; };
|
||||||
7E3C34BC10936AD4F77975F4 /* EmojiMartJSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39001365B76B89983FDB7AD8 /* EmojiMartJSONLoader.swift */; };
|
7E3C34BC10936AD4F77975F4 /* EmojiMartJSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39001365B76B89983FDB7AD8 /* EmojiMartJSONLoader.swift */; };
|
||||||
7E7DF1867F98B0D10A6C0A63 /* FileCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3648F2FADEF2672D6A0D489 /* FileCacheTests.swift */; };
|
7E7DF1867F98B0D10A6C0A63 /* FileCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3648F2FADEF2672D6A0D489 /* FileCacheTests.swift */; };
|
||||||
@@ -301,6 +303,7 @@
|
|||||||
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
|
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
|
||||||
96FE26ABD4E5B8B6EF0EF596 /* RoomMemberDetailsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEFB5144EF8F50C77CF6E14 /* RoomMemberDetailsCoordinator.swift */; };
|
96FE26ABD4E5B8B6EF0EF596 /* RoomMemberDetailsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CEFB5144EF8F50C77CF6E14 /* RoomMemberDetailsCoordinator.swift */; };
|
||||||
97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; };
|
97189E495F0E47805D1868DB /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 527578916BD388A09F5A8036 /* DTCoreText */; };
|
||||||
|
973B5350B5C72E3EB1E62E67 /* ImageProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D28E3A5AE9934EE4FB4720 /* ImageProviderProtocol.swift */; };
|
||||||
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; };
|
978BB24F2A5D31EE59EEC249 /* UserSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4134FEFE4EB55759017408 /* UserSessionProtocol.swift */; };
|
||||||
981853650217B6C8ECDD998C /* NavigationRootCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */; };
|
981853650217B6C8ECDD998C /* NavigationRootCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F875D71347DC81EAE7687446 /* NavigationRootCoordinatorTests.swift */; };
|
||||||
983896D611ABF52A5C37498D /* RoomSummaryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */; };
|
983896D611ABF52A5C37498D /* RoomSummaryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDB3227C7A74B734924942E9 /* RoomSummaryProvider.swift */; };
|
||||||
@@ -315,8 +318,10 @@
|
|||||||
9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */; };
|
9BD3A773186291560DF92B62 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66F2402D738694F98729A441 /* RoomTimelineProvider.swift */; };
|
||||||
9BE7A9CF6C593251D734B461 /* MockServerSelectionScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A20AE75FF4FF35B1FF6CA7 /* MockServerSelectionScreenState.swift */; };
|
9BE7A9CF6C593251D734B461 /* MockServerSelectionScreenState.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A20AE75FF4FF35B1FF6CA7 /* MockServerSelectionScreenState.swift */; };
|
||||||
9C45CE85325CD591DADBC4CA /* ElementXTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */; };
|
9C45CE85325CD591DADBC4CA /* ElementXTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBFEAC3AC691CBB84983E275 /* ElementXTests.swift */; };
|
||||||
|
9C5A07E7C33F3F40287D7861 /* SettingsScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */; };
|
||||||
9CCC77C31CB399661A034739 /* UserProperties+Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6C4BE591FE5C38CE9C7EF3 /* UserProperties+Element.swift */; };
|
9CCC77C31CB399661A034739 /* UserProperties+Element.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A6C4BE591FE5C38CE9C7EF3 /* UserProperties+Element.swift */; };
|
||||||
9D2E03DB175A6AB14589076D /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = AA4E1BEB4E9BC2467006E12B /* AppAuth */; };
|
9D2E03DB175A6AB14589076D /* AppAuth in Frameworks */ = {isa = PBXBuildFile; productRef = AA4E1BEB4E9BC2467006E12B /* AppAuth */; };
|
||||||
|
9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */; };
|
||||||
9D9690D2FD4CD26FF670620F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75EF87651B00A176AB08E97 /* AppDelegate.swift */; };
|
9D9690D2FD4CD26FF670620F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C75EF87651B00A176AB08E97 /* AppDelegate.swift */; };
|
||||||
9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6E4C37E9F0E53D3DF951AC /* HomeScreenUITests.swift */; };
|
9DC5FB22B8F86C3B51E907C1 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D6E4C37E9F0E53D3DF951AC /* HomeScreenUITests.swift */; };
|
||||||
9E8AE387FD03E4F1C1B8815A /* SessionVerificationStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06FCD42EEFEFC220F14EAC5 /* SessionVerificationStateMachine.swift */; };
|
9E8AE387FD03E4F1C1B8815A /* SessionVerificationStateMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06FCD42EEFEFC220F14EAC5 /* SessionVerificationStateMachine.swift */; };
|
||||||
@@ -339,11 +344,13 @@
|
|||||||
A7FD7B992E6EE6E5A8429197 /* RoomSummaryDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142808B69851451AC32A2CEA /* RoomSummaryDetails.swift */; };
|
A7FD7B992E6EE6E5A8429197 /* RoomSummaryDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 142808B69851451AC32A2CEA /* RoomSummaryDetails.swift */; };
|
||||||
A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; };
|
A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; };
|
||||||
A8771F5975A82759FA5138AE /* RoomMemberDetailsScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F19DBE940499D3E3DD405D8 /* RoomMemberDetailsScreenUITests.swift */; };
|
A8771F5975A82759FA5138AE /* RoomMemberDetailsScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0F19DBE940499D3E3DD405D8 /* RoomMemberDetailsScreenUITests.swift */; };
|
||||||
|
A896998A6784DB6F16E912F4 /* MockMediaLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */; };
|
||||||
A8EC7C9D886244DAE9433E37 /* SessionVerificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C18FAAD59AE7F1462D817E /* SessionVerificationViewModel.swift */; };
|
A8EC7C9D886244DAE9433E37 /* SessionVerificationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4C18FAAD59AE7F1462D817E /* SessionVerificationViewModel.swift */; };
|
||||||
A9D23B78F42BCDD896531436 /* UserNotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649759084B0C9FE1F8DF8D17 /* UserNotificationPresenter.swift */; };
|
A9D23B78F42BCDD896531436 /* UserNotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649759084B0C9FE1F8DF8D17 /* UserNotificationPresenter.swift */; };
|
||||||
AA050DF4AEE54A641BA7CA22 /* RoomSummaryProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CC626F97AD70FF0420C115 /* RoomSummaryProviderProtocol.swift */; };
|
AA050DF4AEE54A641BA7CA22 /* RoomSummaryProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10CC626F97AD70FF0420C115 /* RoomSummaryProviderProtocol.swift */; };
|
||||||
AAF0BBED840DF4A53EE85E77 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = C2C69B8BA5A9702E7A8BC08F /* MatrixRustSDK */; };
|
AAF0BBED840DF4A53EE85E77 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = C2C69B8BA5A9702E7A8BC08F /* MatrixRustSDK */; };
|
||||||
AB34401E4E1CAD5D2EC3072B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9760103CF316DF68698BCFE6 /* LaunchScreen.storyboard */; };
|
AB34401E4E1CAD5D2EC3072B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9760103CF316DF68698BCFE6 /* LaunchScreen.storyboard */; };
|
||||||
|
ABDC81BD1C3C8B62665F2C72 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6920A4869821BF72FFC58842 /* MockMediaProvider.swift */; };
|
||||||
ABF3FAB234AD3565B214309B /* TimelineSenderAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BC588051E6572A1AF51D738 /* TimelineSenderAvatarView.swift */; };
|
ABF3FAB234AD3565B214309B /* TimelineSenderAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BC588051E6572A1AF51D738 /* TimelineSenderAvatarView.swift */; };
|
||||||
AC5CC8250CEAE57B73900C57 /* UserNotificationModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD80F22830C2360F3F39DDCE /* UserNotificationModalView.swift */; };
|
AC5CC8250CEAE57B73900C57 /* UserNotificationModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CD80F22830C2360F3F39DDCE /* UserNotificationModalView.swift */; };
|
||||||
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBA759D1347CFFB3D84ED1F /* UserSessionStoreProtocol.swift */; };
|
AC69B6DF15FC451AB2945036 /* UserSessionStoreProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBA759D1347CFFB3D84ED1F /* UserSessionStoreProtocol.swift */; };
|
||||||
@@ -427,6 +434,8 @@
|
|||||||
D85D4FA590305180B4A41795 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3073CCD77D906B330BC1D6 /* Tests.swift */; };
|
D85D4FA590305180B4A41795 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3073CCD77D906B330BC1D6 /* Tests.swift */; };
|
||||||
D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
|
D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; };
|
||||||
D8CFF02C2730EE5BC4F17ABF /* ElementToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */; };
|
D8CFF02C2730EE5BC4F17ABF /* ElementToggleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */; };
|
||||||
|
D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.swift */; };
|
||||||
|
DBAA69CC2CE4D44BC8E20105 /* SettingsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 548E7D356609ACD33AE7643E /* SettingsScreenModels.swift */; };
|
||||||
DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */; };
|
DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */; };
|
||||||
DD9B70DE54B24E0694A35D8A /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
DD9B70DE54B24E0694A35D8A /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
||||||
DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */; };
|
DE4F8C4E0F1DB4832F09DE97 /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */; };
|
||||||
@@ -449,7 +458,6 @@
|
|||||||
E89536FC8C0E4B79E9842A78 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */; };
|
E89536FC8C0E4B79E9842A78 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */; };
|
||||||
E8AB8D16E6D8E8E501F29BD9 /* FileCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B0B1226DA8DB55918B34CD /* FileCache.swift */; };
|
E8AB8D16E6D8E8E501F29BD9 /* FileCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5B0B1226DA8DB55918B34CD /* FileCache.swift */; };
|
||||||
E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */; };
|
E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */; };
|
||||||
E9631F628251F77A24AA4BB4 /* MockMediaProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E36CFF3E430B27B7C3AD0A28 /* MockMediaProxy.swift */; };
|
|
||||||
EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885D8C42DD17625B5261BEFF /* MediaProvider.swift */; };
|
EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885D8C42DD17625B5261BEFF /* MediaProvider.swift */; };
|
||||||
EA31DD9043B91ECB8E45A9A6 /* ScreenshotDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03C9D319676F3C0DC6B0203 /* ScreenshotDetectorTests.swift */; };
|
EA31DD9043B91ECB8E45A9A6 /* ScreenshotDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03C9D319676F3C0DC6B0203 /* ScreenshotDetectorTests.swift */; };
|
||||||
EA65360A0EC026DD83AC0CF5 /* AuthenticationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */; };
|
EA65360A0EC026DD83AC0CF5 /* AuthenticationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */; };
|
||||||
@@ -457,16 +465,19 @@
|
|||||||
EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
|
EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A580295A56B55A856CC4084 /* InfoPlistReader.swift */; };
|
||||||
EC280623A42904341363EAAF /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 886A0A498FA01E8EDD451D05 /* Sentry */; };
|
EC280623A42904341363EAAF /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 886A0A498FA01E8EDD451D05 /* Sentry */; };
|
||||||
EC4C31963E755EEC77BD778C /* AnalyticsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B362E695A7103C11F64B185 /* AnalyticsSettings.swift */; };
|
EC4C31963E755EEC77BD778C /* AnalyticsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B362E695A7103C11F64B185 /* AnalyticsSettings.swift */; };
|
||||||
|
ECA636DAF071C611FDC2BB57 /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
||||||
EE4F5601356228FF72FC56B6 /* MockClientProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F40F48279322E504153AB0D /* MockClientProxy.swift */; };
|
EE4F5601356228FF72FC56B6 /* MockClientProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F40F48279322E504153AB0D /* MockClientProxy.swift */; };
|
||||||
EE6933C935080B4E0348A58B /* EmojiMartCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C3AACCAA82392D08924496 /* EmojiMartCategory.swift */; };
|
EE6933C935080B4E0348A58B /* EmojiMartCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C3AACCAA82392D08924496 /* EmojiMartCategory.swift */; };
|
||||||
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
|
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
|
||||||
EE8A37E2A1A77DE5CF941632 /* StateRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */; };
|
EE8A37E2A1A77DE5CF941632 /* StateRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */; };
|
||||||
EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; };
|
EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; };
|
||||||
|
EEC499F9AC7DD6D18760F81D /* SettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E3CEDE000EFF6E988BEFDE /* SettingsScreenViewModel.swift */; };
|
||||||
EF7924005216B8189898F370 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */; };
|
EF7924005216B8189898F370 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */; };
|
||||||
F040ABFEB0A2B142D948BA12 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; };
|
F040ABFEB0A2B142D948BA12 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; };
|
||||||
F06CE9132855E81EBB6DDC32 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; };
|
F06CE9132855E81EBB6DDC32 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 800631D7250B7F93195035F1 /* KeychainAccess */; };
|
||||||
F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */; };
|
F07D88421A9BC4D03D4A5055 /* VideoRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */; };
|
||||||
F0F82C3C848C865C3098AA52 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 67E7A6F388D3BF85767609D9 /* Sentry */; };
|
F0F82C3C848C865C3098AA52 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 67E7A6F388D3BF85767609D9 /* Sentry */; };
|
||||||
|
F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D52BAA5BADB06E5E8C295D /* Assets.swift */; };
|
||||||
F257F964493A9CD02A6F720C /* OnboardingPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF2717AB91060260E5F4781 /* OnboardingPageView.swift */; };
|
F257F964493A9CD02A6F720C /* OnboardingPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF2717AB91060260E5F4781 /* OnboardingPageView.swift */; };
|
||||||
F32B271F60531BE92C6E62A1 /* StickerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 612EF972F2A1800682D32C5E /* StickerRoomTimelineView.swift */; };
|
F32B271F60531BE92C6E62A1 /* StickerRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 612EF972F2A1800682D32C5E /* StickerRoomTimelineView.swift */; };
|
||||||
F425C3F85BFF28C9AC593F52 /* MockNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96561CC53F7C1E24D4C292E4 /* MockNotificationManager.swift */; };
|
F425C3F85BFF28C9AC593F52 /* MockNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96561CC53F7C1E24D4C292E4 /* MockNotificationManager.swift */; };
|
||||||
@@ -475,12 +486,10 @@
|
|||||||
F656F92A63D3DC1978D79427 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = 2A3F7BCCB18C15B30CCA39A9 /* AnalyticsEvents */; };
|
F656F92A63D3DC1978D79427 /* AnalyticsEvents in Frameworks */ = {isa = PBXBuildFile; productRef = 2A3F7BCCB18C15B30CCA39A9 /* AnalyticsEvents */; };
|
||||||
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
|
F6F49E37272AD7397CD29A01 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */; };
|
||||||
F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */; };
|
F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */; };
|
||||||
F9981191DC408AED537C1749 /* MediaProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */; };
|
|
||||||
F99FB21EFC6D99D247FE7CBE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DE8DC9B3FBA402117DC4C49F /* Kingfisher */; };
|
F99FB21EFC6D99D247FE7CBE /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = DE8DC9B3FBA402117DC4C49F /* Kingfisher */; };
|
||||||
F9F6D2883BBEBB9A3789A137 /* OnboardingViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A941F289F6AB876BA3361A /* OnboardingViewModelTests.swift */; };
|
F9F6D2883BBEBB9A3789A137 /* OnboardingViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00A941F289F6AB876BA3361A /* OnboardingViewModelTests.swift */; };
|
||||||
FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */; };
|
FA2BBAE9FC5E2E9F960C0980 /* NavigationCoordinators.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8F28602AC7AC881AED37EBA /* NavigationCoordinators.swift */; };
|
||||||
FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; };
|
FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; };
|
||||||
FBCD77D557AACBE9B445133A /* MediaProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */; };
|
|
||||||
FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; };
|
FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; };
|
||||||
FC10228E73323BDC09526F97 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = 9C73F37731C9FDED1BB24C1C /* Collections */; };
|
FC10228E73323BDC09526F97 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = 9C73F37731C9FDED1BB24C1C /* Collections */; };
|
||||||
FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; };
|
FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; };
|
||||||
@@ -549,11 +558,9 @@
|
|||||||
086B997409328F091EBA43CE /* RoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenUITests.swift; sourceTree = "<group>"; };
|
086B997409328F091EBA43CE /* RoomScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
08F64963396A6A23538EFCEC /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = is; path = is.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
08F64963396A6A23538EFCEC /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = is; path = is.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
09199C43BAB209C0BD89A836 /* OnboardingPageIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageIndicator.swift; sourceTree = "<group>"; };
|
09199C43BAB209C0BD89A836 /* OnboardingPageIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingPageIndicator.swift; sourceTree = "<group>"; };
|
||||||
0950733DD4BA83EEE752E259 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = "<group>"; };
|
|
||||||
095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = "<group>"; };
|
095AED4CF56DFF3EB7BB84C8 /* RoomTimelineProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineProviderProtocol.swift; sourceTree = "<group>"; };
|
||||||
0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementToggleStyle.swift; sourceTree = "<group>"; };
|
0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementToggleStyle.swift; sourceTree = "<group>"; };
|
||||||
099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = "<group>"; };
|
099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = "<group>"; };
|
||||||
0A191D3FDB995309C7E2DE7D /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
0AB7A0C06CB527A1095DEB33 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
0AB7A0C06CB527A1095DEB33 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
0B490675B8E31423AF116BDA /* NotificationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceProxy.swift; sourceTree = "<group>"; };
|
0B490675B8E31423AF116BDA /* NotificationServiceProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationServiceProxy.swift; sourceTree = "<group>"; };
|
||||||
0B869438A1B52836F912A702 /* MockSoftLogoutScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSoftLogoutScreenState.swift; sourceTree = "<group>"; };
|
0B869438A1B52836F912A702 /* MockSoftLogoutScreenState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSoftLogoutScreenState.swift; sourceTree = "<group>"; };
|
||||||
@@ -624,6 +631,7 @@
|
|||||||
287FC98AF2664EAD79C0D902 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
287FC98AF2664EAD79C0D902 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = "<group>"; };
|
||||||
28959C7DB36C7688A01D4045 /* BugReportViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportViewModelProtocol.swift; sourceTree = "<group>"; };
|
28959C7DB36C7688A01D4045 /* BugReportViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
28EA8BE9EEDBD17555141C7E /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = el; path = el.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
28EA8BE9EEDBD17555141C7E /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = el; path = el.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
|
2A46F50F2A46103ADF143ADB /* MediaLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoader.swift; sourceTree = "<group>"; };
|
||||||
2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilder.swift; sourceTree = "<group>"; };
|
2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilder.swift; sourceTree = "<group>"; };
|
||||||
2AE83A3DD63BCFBB956FE5CB /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
2AE83A3DD63BCFBB956FE5CB /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = nl; path = nl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
2AEA20A6B4883E60469ACF8F /* SoftLogoutCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutCoordinator.swift; sourceTree = "<group>"; };
|
2AEA20A6B4883E60469ACF8F /* SoftLogoutCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@@ -669,10 +677,10 @@
|
|||||||
3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelTests.swift; sourceTree = "<group>"; };
|
3D487C1185D658F8B15B8F55 /* SettingsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
3D4DD336905C72F95EAF34B7 /* ElementX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElementX-Bridging-Header.h"; sourceTree = "<group>"; };
|
3D4DD336905C72F95EAF34B7 /* ElementX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ElementX-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
|
3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
|
||||||
3DD2D50A7EAA4FC78417730E /* SettingsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCoordinator.swift; sourceTree = "<group>"; };
|
|
||||||
3DD6E7C1D8B53F47789778CD /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-CA"; path = "fr-CA.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
3DD6E7C1D8B53F47789778CD /* fr-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "fr-CA"; path = "fr-CA.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTaskService.swift; sourceTree = "<group>"; };
|
3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTaskService.swift; sourceTree = "<group>"; };
|
||||||
3DFE4453AB0B34C203447162 /* ImageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineItem.swift; sourceTree = "<group>"; };
|
3DFE4453AB0B34C203447162 /* ImageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
|
3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderTests.swift; sourceTree = "<group>"; };
|
||||||
3F40F48279322E504153AB0D /* MockClientProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockClientProxy.swift; sourceTree = "<group>"; };
|
3F40F48279322E504153AB0D /* MockClientProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockClientProxy.swift; sourceTree = "<group>"; };
|
||||||
3F9E67AAB66638C69626866C /* UserSessionFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinator.swift; sourceTree = "<group>"; };
|
3F9E67AAB66638C69626866C /* UserSessionFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||||
3FEE631F3A4AFDC6652DD9DA /* RoomTimelineViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewFactory.swift; sourceTree = "<group>"; };
|
3FEE631F3A4AFDC6652DD9DA /* RoomTimelineViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewFactory.swift; sourceTree = "<group>"; };
|
||||||
@@ -699,10 +707,10 @@
|
|||||||
48CE6BF18E542B32FA52CE06 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fa; path = fa.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
48CE6BF18E542B32FA52CE06 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = fa; path = fa.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
49193CB0C248D621A96FB2AA /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
|
49193CB0C248D621A96FB2AA /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
4959CECEC984B3995616F427 /* DataProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProtectionManager.swift; sourceTree = "<group>"; };
|
4959CECEC984B3995616F427 /* DataProtectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataProtectionManager.swift; sourceTree = "<group>"; };
|
||||||
4990FDBDA96B88E214F92F48 /* SettingsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsModels.swift; sourceTree = "<group>"; };
|
|
||||||
49D2C8E66E83EA578A7F318A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
49D2C8E66E83EA578A7F318A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationRequest.swift; sourceTree = "<group>"; };
|
49E751D7EDB6043238111D90 /* UNNotificationRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNNotificationRequest.swift; sourceTree = "<group>"; };
|
||||||
4A57A4AFA6A068668AFBD070 /* UIActivityViewControllerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityViewControllerWrapper.swift; sourceTree = "<group>"; };
|
4A57A4AFA6A068668AFBD070 /* UIActivityViewControllerWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityViewControllerWrapper.swift; sourceTree = "<group>"; };
|
||||||
|
4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaLoader.swift; sourceTree = "<group>"; };
|
||||||
4B362E695A7103C11F64B185 /* AnalyticsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettings.swift; sourceTree = "<group>"; };
|
4B362E695A7103C11F64B185 /* AnalyticsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsSettings.swift; sourceTree = "<group>"; };
|
||||||
4B40B7F6FCCE2D8C242492D9 /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ga; path = ga.lproj/Localizable.strings; sourceTree = "<group>"; };
|
4B40B7F6FCCE2D8C242492D9 /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ga; path = ga.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
4B41FABA2B0AEF4389986495 /* LoginMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMode.swift; sourceTree = "<group>"; };
|
4B41FABA2B0AEF4389986495 /* LoginMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginMode.swift; sourceTree = "<group>"; };
|
||||||
@@ -716,6 +724,7 @@
|
|||||||
4F5F0662483ED69791D63B16 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = et; path = et.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
4F5F0662483ED69791D63B16 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = et; path = et.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
|
505208F28007C0FEC14E1FF0 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItem.swift; sourceTree = "<group>"; };
|
5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
|
50E3CEDE000EFF6E988BEFDE /* SettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||||
51DF91C374901E94D93276F1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-MX"; path = "es-MX.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
51DF91C374901E94D93276F1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "es-MX"; path = "es-MX.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
|
||||||
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = "<group>"; };
|
5221DFDF809142A2D6AC82B9 /* RoomScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = "<group>"; };
|
||||||
529513218340CC8419273165 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
529513218340CC8419273165 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@@ -728,6 +737,7 @@
|
|||||||
541542F5AC323709D8563458 /* AnalyticsPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPrompt.swift; sourceTree = "<group>"; };
|
541542F5AC323709D8563458 /* AnalyticsPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPrompt.swift; sourceTree = "<group>"; };
|
||||||
542D4F49FABA056DEEEB3400 /* RustTracing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RustTracing.swift; sourceTree = "<group>"; };
|
542D4F49FABA056DEEEB3400 /* RustTracing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RustTracing.swift; sourceTree = "<group>"; };
|
||||||
5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = "<group>"; };
|
5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = "<group>"; };
|
||||||
|
548E7D356609ACD33AE7643E /* SettingsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenModels.swift; sourceTree = "<group>"; };
|
||||||
55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemSender.swift; sourceTree = "<group>"; };
|
55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemSender.swift; sourceTree = "<group>"; };
|
||||||
55BC11560C8A2598964FFA4C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
55BC11560C8A2598964FFA4C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
55D7187F6B0C0A651AC3DFFA /* in */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = in; path = in.lproj/Localizable.strings; sourceTree = "<group>"; };
|
55D7187F6B0C0A651AC3DFFA /* in */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = in; path = in.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
@@ -738,7 +748,6 @@
|
|||||||
5773C86AF04AEF26515AD00C /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
5773C86AF04AEF26515AD00C /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
57CF0E6DD78FB3F6CBF5AC38 /* RoomMemberDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsViewModel.swift; sourceTree = "<group>"; };
|
57CF0E6DD78FB3F6CBF5AC38 /* RoomMemberDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsViewModel.swift; sourceTree = "<group>"; };
|
||||||
57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceLocator.swift; sourceTree = "<group>"; };
|
57F95CADD0A5DBD76B990FCB /* ServiceLocator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceLocator.swift; sourceTree = "<group>"; };
|
||||||
5B2F9D5C39A4494D19F33E38 /* SettingsViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModelProtocol.swift; sourceTree = "<group>"; };
|
|
||||||
5D26A086A8278D39B5756D6F /* project.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = project.yml; sourceTree = "<group>"; };
|
5D26A086A8278D39B5756D6F /* project.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = project.yml; sourceTree = "<group>"; };
|
||||||
5D2D0A6F1ABC99D29462FB84 /* AuthenticationCoordinatorUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinatorUITests.swift; sourceTree = "<group>"; };
|
5D2D0A6F1ABC99D29462FB84 /* AuthenticationCoordinatorUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinatorUITests.swift; sourceTree = "<group>"; };
|
||||||
5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProperties.swift; sourceTree = "<group>"; };
|
5DE8D25D6A91030175D52A20 /* RoomTimelineItemProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProperties.swift; sourceTree = "<group>"; };
|
||||||
@@ -774,6 +783,7 @@
|
|||||||
6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateStoreViewModel.swift; sourceTree = "<group>"; };
|
6F3DFE5B444F131648066F05 /* StateStoreViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateStoreViewModel.swift; sourceTree = "<group>"; };
|
||||||
6FB31A32C93D94930B253FBF /* PermalinkBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermalinkBuilderTests.swift; sourceTree = "<group>"; };
|
6FB31A32C93D94930B253FBF /* PermalinkBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermalinkBuilderTests.swift; sourceTree = "<group>"; };
|
||||||
6FC5015B9634698BDB8701AF /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
6FC5015B9634698BDB8701AF /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = it; path = it.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
|
6FF540C393C7DDEE9C902DFF /* SettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
71556206CD5E8B1F53F07178 /* MockRoomTimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineControllerFactory.swift; sourceTree = "<group>"; };
|
71556206CD5E8B1F53F07178 /* MockRoomTimelineControllerFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineControllerFactory.swift; sourceTree = "<group>"; };
|
||||||
71BC7CA1BC1041E93077BBA1 /* HomeScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenModels.swift; sourceTree = "<group>"; };
|
71BC7CA1BC1041E93077BBA1 /* HomeScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenModels.swift; sourceTree = "<group>"; };
|
||||||
71D52BAA5BADB06E5E8C295D /* Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; };
|
71D52BAA5BADB06E5E8C295D /* Assets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Assets.swift; sourceTree = "<group>"; };
|
||||||
@@ -814,6 +824,7 @@
|
|||||||
8872E9C5E91E9F2BFC4EBCCA /* AlignedScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignedScrollView.swift; sourceTree = "<group>"; };
|
8872E9C5E91E9F2BFC4EBCCA /* AlignedScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlignedScrollView.swift; sourceTree = "<group>"; };
|
||||||
8888D13645C04AC9818F5778 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
8888D13645C04AC9818F5778 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
892E29C98C4E8182C9037F84 /* TimelineStyler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyler.swift; sourceTree = "<group>"; };
|
892E29C98C4E8182C9037F84 /* TimelineStyler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyler.swift; sourceTree = "<group>"; };
|
||||||
|
8A3DF4FAB9FBEF782DF08F3A /* MediaLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaLoaderProtocol.swift; sourceTree = "<group>"; };
|
||||||
8A9AE4967817E9608E22EB44 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
8A9AE4967817E9608E22EB44 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||||
8AC1A01C3A745BDF1D3697D3 /* SessionVerificationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreen.swift; sourceTree = "<group>"; };
|
8AC1A01C3A745BDF1D3697D3 /* SessionVerificationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreen.swift; sourceTree = "<group>"; };
|
||||||
8C0AA893D6F8A2F563E01BB9 /* in */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = in; path = in.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
8C0AA893D6F8A2F563E01BB9 /* in */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = in; path = in.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
@@ -822,6 +833,7 @@
|
|||||||
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
8D8169443E5AC5FF71BFB3DB /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
|
8DC2C9E0E15C79BBDA80F0A2 /* TimelineStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStyle.swift; sourceTree = "<group>"; };
|
||||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
||||||
|
8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenUITests.swift; sourceTree = "<group>"; };
|
||||||
8ED2D2F6A137A95EA50413BE /* UserNotificationControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationControllerProtocol.swift; sourceTree = "<group>"; };
|
8ED2D2F6A137A95EA50413BE /* UserNotificationControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationControllerProtocol.swift; sourceTree = "<group>"; };
|
||||||
8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomSummaryProvider.swift; sourceTree = "<group>"; };
|
8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomSummaryProvider.swift; sourceTree = "<group>"; };
|
||||||
8FC26871038FB0E4AAE22605 /* apple_emojis_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
|
8FC26871038FB0E4AAE22605 /* apple_emojis_data.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = apple_emojis_data.json; sourceTree = "<group>"; };
|
||||||
@@ -918,6 +930,7 @@
|
|||||||
B4C18FAAD59AE7F1462D817E /* SessionVerificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModel.swift; sourceTree = "<group>"; };
|
B4C18FAAD59AE7F1462D817E /* SessionVerificationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationViewModel.swift; sourceTree = "<group>"; };
|
||||||
B4DE1CF8F5EFD353B1A5E36F /* AnalyticsPromptCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptCoordinator.swift; sourceTree = "<group>"; };
|
B4DE1CF8F5EFD353B1A5E36F /* AnalyticsPromptCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptCoordinator.swift; sourceTree = "<group>"; };
|
||||||
B516212D9FE785DDD5E490D1 /* BugReportModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportModels.swift; sourceTree = "<group>"; };
|
B516212D9FE785DDD5E490D1 /* BugReportModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportModels.swift; sourceTree = "<group>"; };
|
||||||
|
B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = "<group>"; };
|
||||||
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = "<group>"; };
|
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
|
B6E89E530A8E92EC44301CA1 /* Bundle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
|
||||||
@@ -944,6 +957,7 @@
|
|||||||
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
|
C070FD43DC6BF4E50217965A /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
|
||||||
C1198B925F4A88DA74083662 /* OnboardingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewModel.swift; sourceTree = "<group>"; };
|
C1198B925F4A88DA74083662 /* OnboardingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewModel.swift; sourceTree = "<group>"; };
|
||||||
C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = "<group>"; };
|
C2886615BEBAE33A0AA4D5F8 /* RoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = "<group>"; };
|
||||||
|
C352359663A0E52BA20761EE /* LoadableImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableImage.swift; sourceTree = "<group>"; };
|
||||||
C38AE3617D7619EF30CDD229 /* EmojiMartStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartStore.swift; sourceTree = "<group>"; };
|
C38AE3617D7619EF30CDD229 /* EmojiMartStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiMartStore.swift; sourceTree = "<group>"; };
|
||||||
C3F652E88106B855A2A55ADE /* FilePreviewViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewViewModelProtocol.swift; sourceTree = "<group>"; };
|
C3F652E88106B855A2A55ADE /* FilePreviewViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
C483956FA3D665E3842E319A /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
|
C483956FA3D665E3842E319A /* SettingsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = "<group>"; };
|
||||||
@@ -951,6 +965,7 @@
|
|||||||
C687844F60BFF532D49A994C /* AnalyticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsTests.swift; sourceTree = "<group>"; };
|
C687844F60BFF532D49A994C /* AnalyticsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsTests.swift; sourceTree = "<group>"; };
|
||||||
C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
|
C6A9F49B3EE59147AF2F70BB /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportUITests.swift; sourceTree = "<group>"; };
|
C6FEA87EA3752203065ECE27 /* BugReportUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportUITests.swift; sourceTree = "<group>"; };
|
||||||
|
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderAvatarImage.swift; sourceTree = "<group>"; };
|
||||||
C75EF87651B00A176AB08E97 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
C75EF87651B00A176AB08E97 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
|
C789E7BFC066CF39B8AE0974 /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
|
||||||
C830A64609CBD152F06E0457 /* NotificationConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConstants.swift; sourceTree = "<group>"; };
|
C830A64609CBD152F06E0457 /* NotificationConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationConstants.swift; sourceTree = "<group>"; };
|
||||||
@@ -959,6 +974,7 @@
|
|||||||
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
|
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedactedRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
C95ADE8D9527523572532219 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
C95ADE8D9527523572532219 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = hu; path = hu.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
C9A86C95340248A8B7BA9A43 /* AnalyticsPromptViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptViewModelProtocol.swift; sourceTree = "<group>"; };
|
C9A86C95340248A8B7BA9A43 /* AnalyticsPromptViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
C9F72B9701D847C591ABE1A8 /* SettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||||
CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinator.swift; sourceTree = "<group>"; };
|
CA28F29C9F93E93CC3C2C715 /* NavigationRootCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationRootCoordinator.swift; sourceTree = "<group>"; };
|
||||||
CA78F8D91974DFFEDC05485A /* RoomMemberDetailsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsModels.swift; sourceTree = "<group>"; };
|
CA78F8D91974DFFEDC05485A /* RoomMemberDetailsModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsModels.swift; sourceTree = "<group>"; };
|
||||||
CA89A2DD51B6BBE1DA55E263 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
CA89A2DD51B6BBE1DA55E263 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
|
||||||
@@ -985,6 +1001,7 @@
|
|||||||
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = "<group>"; };
|
D3D455BC2423D911A62ACFB2 /* NSELogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSELogger.swift; sourceTree = "<group>"; };
|
||||||
D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
D4DA544B2520BFA65D6DB4BB /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||||
D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
D5AC06FC11B6638F7BF1670E /* TimelineDeliveryStatusView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineDeliveryStatusView.swift; sourceTree = "<group>"; };
|
||||||
|
D5D28E3A5AE9934EE4FB4720 /* ImageProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProviderProtocol.swift; sourceTree = "<group>"; };
|
||||||
D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
|
D653265D006E708E4E51AD64 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||||
D67CBAFA48ED0B6FCE74F88F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
D67CBAFA48ED0B6FCE74F88F /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinator.swift; sourceTree = "<group>"; };
|
D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinator.swift; sourceTree = "<group>"; };
|
||||||
@@ -1006,15 +1023,12 @@
|
|||||||
DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewViewModelTests.swift; sourceTree = "<group>"; };
|
DF38B69D2C331A499276F400 /* FilePreviewViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewViewModelTests.swift; sourceTree = "<group>"; };
|
||||||
DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineItem.swift; sourceTree = "<group>"; };
|
DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = "<group>"; };
|
E0FCA0957FAA0E15A9F5579D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Untranslated.stringsdict; sourceTree = "<group>"; };
|
||||||
E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProxy.swift; sourceTree = "<group>"; };
|
|
||||||
E157152B11E347F735C3FD6E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
E157152B11E347F735C3FD6E /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = tr; path = tr.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||||
E18CF12478983A5EB390FB26 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = "<group>"; };
|
E18CF12478983A5EB390FB26 /* MessageComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageComposer.swift; sourceTree = "<group>"; };
|
||||||
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSize.swift; sourceTree = "<group>"; };
|
E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarSize.swift; sourceTree = "<group>"; };
|
||||||
E26747B3154A5DBC3A7E24A5 /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = "<group>"; };
|
E26747B3154A5DBC3A7E24A5 /* Image.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Image.swift; sourceTree = "<group>"; };
|
||||||
E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = "<group>"; };
|
E36CB905A2B9EC2C92A2DA7C /* KeychainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = "<group>"; };
|
||||||
E36CFF3E430B27B7C3AD0A28 /* MockMediaProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaProxy.swift; sourceTree = "<group>"; };
|
|
||||||
E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsClientProtocol.swift; sourceTree = "<group>"; };
|
E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsClientProtocol.swift; sourceTree = "<group>"; };
|
||||||
E3E29F98CF0E960689A410E3 /* SettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsUITests.swift; sourceTree = "<group>"; };
|
|
||||||
E45C57120F28F8D619150219 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
E45C57120F28F8D619150219 /* sr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sr; path = sr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
E4BB9A17AC512A7EF4B106E5 /* SessionVerificationModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationModels.swift; sourceTree = "<group>"; };
|
E4BB9A17AC512A7EF4B106E5 /* SessionVerificationModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationModels.swift; sourceTree = "<group>"; };
|
||||||
E51E3D86A84341C3A0CB8A40 /* FileRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineView.swift; sourceTree = "<group>"; };
|
E51E3D86A84341C3A0CB8A40 /* FileRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
@@ -1070,7 +1084,6 @@
|
|||||||
F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineView.swift; sourceTree = "<group>"; };
|
F9ED8E731E21055F728E5FED /* TimelineStartRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStartRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
FAB10E673916D2B8D21FD197 /* TemplateModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModels.swift; sourceTree = "<group>"; };
|
FAB10E673916D2B8D21FD197 /* TemplateModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModels.swift; sourceTree = "<group>"; };
|
||||||
FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorProtocol.swift; sourceTree = "<group>"; };
|
FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorProtocol.swift; sourceTree = "<group>"; };
|
||||||
FC3D31C2DA6910AA0079678A /* MediaProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProxyProtocol.swift; sourceTree = "<group>"; };
|
|
||||||
FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerTests.swift; sourceTree = "<group>"; };
|
FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerTests.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -1307,6 +1320,9 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */,
|
0960A7F5C1B0B6679BDF26F9 /* ElementToggleStyle.swift */,
|
||||||
|
B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */,
|
||||||
|
C352359663A0E52BA20761EE /* LoadableImage.swift */,
|
||||||
|
C705E605EF57C19DBE86FFA1 /* PlaceholderAvatarImage.swift */,
|
||||||
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */,
|
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */,
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
@@ -1657,10 +1673,10 @@
|
|||||||
70B74A432C241E56A7ACE610 /* Settings */ = {
|
70B74A432C241E56A7ACE610 /* Settings */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3DD2D50A7EAA4FC78417730E /* SettingsCoordinator.swift */,
|
6FF540C393C7DDEE9C902DFF /* SettingsScreenCoordinator.swift */,
|
||||||
4990FDBDA96B88E214F92F48 /* SettingsModels.swift */,
|
548E7D356609ACD33AE7643E /* SettingsScreenModels.swift */,
|
||||||
0A191D3FDB995309C7E2DE7D /* SettingsViewModel.swift */,
|
50E3CEDE000EFF6E988BEFDE /* SettingsScreenViewModel.swift */,
|
||||||
5B2F9D5C39A4494D19F33E38 /* SettingsViewModelProtocol.swift */,
|
C9F72B9701D847C591ABE1A8 /* SettingsScreenViewModelProtocol.swift */,
|
||||||
4541090DFE1A5499BD67BD14 /* View */,
|
4541090DFE1A5499BD67BD14 /* View */,
|
||||||
);
|
);
|
||||||
path = Settings;
|
path = Settings;
|
||||||
@@ -1735,11 +1751,12 @@
|
|||||||
7583EAC171059A86B767209F /* MediaProvider */ = {
|
7583EAC171059A86B767209F /* MediaProvider */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */,
|
||||||
62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */,
|
62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */,
|
||||||
3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */,
|
3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */,
|
||||||
AEC96B3DC55090BBF8876CC2 /* MockFileCache.swift */,
|
AEC96B3DC55090BBF8876CC2 /* MockFileCache.swift */,
|
||||||
AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */,
|
AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */,
|
||||||
E36CFF3E430B27B7C3AD0A28 /* MockMediaProxy.swift */,
|
4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */,
|
||||||
);
|
);
|
||||||
path = MediaProvider;
|
path = MediaProvider;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1808,10 +1825,11 @@
|
|||||||
79E560F5113ED25D172E550C /* Media */ = {
|
79E560F5113ED25D172E550C /* Media */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
D5D28E3A5AE9934EE4FB4720 /* ImageProviderProtocol.swift */,
|
||||||
|
2A46F50F2A46103ADF143ADB /* MediaLoader.swift */,
|
||||||
|
8A3DF4FAB9FBEF782DF08F3A /* MediaLoaderProtocol.swift */,
|
||||||
885D8C42DD17625B5261BEFF /* MediaProvider.swift */,
|
885D8C42DD17625B5261BEFF /* MediaProvider.swift */,
|
||||||
C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */,
|
C888BCD78E2A55DCE364F160 /* MediaProviderProtocol.swift */,
|
||||||
E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */,
|
|
||||||
FC3D31C2DA6910AA0079678A /* MediaProxyProtocol.swift */,
|
|
||||||
179423E34EE846E048E49CBF /* MediaSourceProxy.swift */,
|
179423E34EE846E048E49CBF /* MediaSourceProxy.swift */,
|
||||||
6920A4869821BF72FFC58842 /* MockMediaProvider.swift */,
|
6920A4869821BF72FFC58842 /* MockMediaProvider.swift */,
|
||||||
);
|
);
|
||||||
@@ -1991,7 +2009,7 @@
|
|||||||
086B997409328F091EBA43CE /* RoomScreenUITests.swift */,
|
086B997409328F091EBA43CE /* RoomScreenUITests.swift */,
|
||||||
054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */,
|
054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */,
|
||||||
6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */,
|
6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */,
|
||||||
E3E29F98CF0E960689A410E3 /* SettingsUITests.swift */,
|
8EC57A32ABC80D774CC663DB /* SettingsScreenUITests.swift */,
|
||||||
55F30E764BED111C81739844 /* SoftLogoutUITests.swift */,
|
55F30E764BED111C81739844 /* SoftLogoutUITests.swift */,
|
||||||
F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */,
|
F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */,
|
||||||
);
|
);
|
||||||
@@ -2183,7 +2201,6 @@
|
|||||||
D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */,
|
D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */,
|
||||||
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */,
|
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */,
|
||||||
42EEA67A6796BDC2761619C5 /* PaginationIndicatorRoomTimelineView.swift */,
|
42EEA67A6796BDC2761619C5 /* PaginationIndicatorRoomTimelineView.swift */,
|
||||||
0950733DD4BA83EEE752E259 /* PlaceholderAvatarImage.swift */,
|
|
||||||
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */,
|
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */,
|
||||||
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */,
|
C8F2A7A4E3F5060F52ACFFB0 /* RedactedRoomTimelineView.swift */,
|
||||||
6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */,
|
6390A6DC140CA3D6865A66FF /* SeparatorRoomTimelineView.swift */,
|
||||||
@@ -2875,6 +2892,7 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
F253AAB4C8F06208173C9C4A /* Assets.swift in Sources */,
|
||||||
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */,
|
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */,
|
||||||
EF7924005216B8189898F370 /* BackgroundTaskProtocol.swift in Sources */,
|
EF7924005216B8189898F370 /* BackgroundTaskProtocol.swift in Sources */,
|
||||||
1B4B3E847BF944DB2C1C217F /* BackgroundTaskServiceProtocol.swift in Sources */,
|
1B4B3E847BF944DB2C1C217F /* BackgroundTaskServiceProtocol.swift in Sources */,
|
||||||
@@ -2884,16 +2902,18 @@
|
|||||||
E8AB8D16E6D8E8E501F29BD9 /* FileCache.swift in Sources */,
|
E8AB8D16E6D8E8E501F29BD9 /* FileCache.swift in Sources */,
|
||||||
A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */,
|
A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */,
|
||||||
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */,
|
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */,
|
||||||
|
1FD56B9EA4CA804120A2E743 /* ImageProviderProtocol.swift in Sources */,
|
||||||
EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */,
|
EBE13FAB4E29738AC41BD3E5 /* InfoPlistReader.swift in Sources */,
|
||||||
8691186F9B99BCDDB7CACDD8 /* KeychainController.swift in Sources */,
|
8691186F9B99BCDDB7CACDD8 /* KeychainController.swift in Sources */,
|
||||||
A440D4BC02088482EC633A88 /* KeychainControllerProtocol.swift in Sources */,
|
A440D4BC02088482EC633A88 /* KeychainControllerProtocol.swift in Sources */,
|
||||||
AD2A81B65A9F6163012086F1 /* MXLog.swift in Sources */,
|
AD2A81B65A9F6163012086F1 /* MXLog.swift in Sources */,
|
||||||
8C454500B8073E1201F801A9 /* MXLogger.swift in Sources */,
|
8C454500B8073E1201F801A9 /* MXLogger.swift in Sources */,
|
||||||
|
20840B4549FFF1301D0A5FF2 /* MediaLoader.swift in Sources */,
|
||||||
|
5B300BACD8A1B252AC95FA34 /* MediaLoaderProtocol.swift in Sources */,
|
||||||
0F3F2FDD4021A25A0D57F801 /* MediaProvider.swift in Sources */,
|
0F3F2FDD4021A25A0D57F801 /* MediaProvider.swift in Sources */,
|
||||||
4C3365818DE1CEAEDF590FD3 /* MediaProviderProtocol.swift in Sources */,
|
4C3365818DE1CEAEDF590FD3 /* MediaProviderProtocol.swift in Sources */,
|
||||||
F9981191DC408AED537C1749 /* MediaProxy.swift in Sources */,
|
|
||||||
2CA8AD07773A38BA4662098B /* MediaProxyProtocol.swift in Sources */,
|
|
||||||
5E25568E1CDAD983517E58B5 /* MediaSourceProxy.swift in Sources */,
|
5E25568E1CDAD983517E58B5 /* MediaSourceProxy.swift in Sources */,
|
||||||
|
ABDC81BD1C3C8B62665F2C72 /* MockMediaProvider.swift in Sources */,
|
||||||
5455147CAC63F71E48F7D699 /* NSELogger.swift in Sources */,
|
5455147CAC63F71E48F7D699 /* NSELogger.swift in Sources */,
|
||||||
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */,
|
5C02841B2A86327B2C377682 /* NotificationConstants.swift in Sources */,
|
||||||
214CDBF0C783155242FFE4A0 /* NotificationItemProxy+NSE.swift in Sources */,
|
214CDBF0C783155242FFE4A0 /* NotificationItemProxy+NSE.swift in Sources */,
|
||||||
@@ -2903,6 +2923,8 @@
|
|||||||
7E3B1F8D72573ED2FCB2D94B /* NotificationServiceProxyProtocol.swift in Sources */,
|
7E3B1F8D72573ED2FCB2D94B /* NotificationServiceProxyProtocol.swift in Sources */,
|
||||||
414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */,
|
414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */,
|
||||||
7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */,
|
7354D094A4C59B555F407FA1 /* RustTracing.swift in Sources */,
|
||||||
|
ECA636DAF071C611FDC2BB57 /* Strings+Untranslated.swift in Sources */,
|
||||||
|
6EC7A40A537CFB3D526A111C /* Strings.swift in Sources */,
|
||||||
719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */,
|
719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */,
|
||||||
BA074E9812F96FFA3200ED1D /* TimelineItemProxy.swift in Sources */,
|
BA074E9812F96FFA3200ED1D /* TimelineItemProxy.swift in Sources */,
|
||||||
B8C316C6CA24512DFE9A27FD /* TimelineItemSender.swift in Sources */,
|
B8C316C6CA24512DFE9A27FD /* TimelineItemSender.swift in Sources */,
|
||||||
@@ -2934,12 +2956,13 @@
|
|||||||
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */,
|
149D1942DC005D0485FB8D93 /* LoggingTests.swift in Sources */,
|
||||||
1E59B77A0B2CE83DCC1B203C /* LoginViewModelTests.swift in Sources */,
|
1E59B77A0B2CE83DCC1B203C /* LoginViewModelTests.swift in Sources */,
|
||||||
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */,
|
2E43A3D221BE9587BC19C3F1 /* MatrixEntityRegexTests.swift in Sources */,
|
||||||
|
4B978C09567387EF4366BD7A /* MediaLoaderTests.swift in Sources */,
|
||||||
167D00CAA13FAFB822298021 /* MediaProviderTests.swift in Sources */,
|
167D00CAA13FAFB822298021 /* MediaProviderTests.swift in Sources */,
|
||||||
4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */,
|
4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */,
|
||||||
1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */,
|
1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */,
|
||||||
CF6319CC05F964B4D05BF614 /* MockFileCache.swift in Sources */,
|
CF6319CC05F964B4D05BF614 /* MockFileCache.swift in Sources */,
|
||||||
DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */,
|
DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */,
|
||||||
E9631F628251F77A24AA4BB4 /* MockMediaProxy.swift in Sources */,
|
A896998A6784DB6F16E912F4 /* MockMediaLoader.swift in Sources */,
|
||||||
981853650217B6C8ECDD998C /* NavigationRootCoordinatorTests.swift in Sources */,
|
981853650217B6C8ECDD998C /* NavigationRootCoordinatorTests.swift in Sources */,
|
||||||
69C7B956B74BEC3DB88224EA /* NavigationSplitCoordinatorTests.swift in Sources */,
|
69C7B956B74BEC3DB88224EA /* NavigationSplitCoordinatorTests.swift in Sources */,
|
||||||
4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */,
|
4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */,
|
||||||
@@ -3065,6 +3088,7 @@
|
|||||||
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */,
|
56F0A22972A3BB519DA2261C /* HomeScreenViewModelProtocol.swift in Sources */,
|
||||||
03D684A3AE85A23B3DA3B43F /* Image.swift in Sources */,
|
03D684A3AE85A23B3DA3B43F /* Image.swift in Sources */,
|
||||||
BA31448FBD9697F8CB9A83CD /* ImageCache.swift in Sources */,
|
BA31448FBD9697F8CB9A83CD /* ImageCache.swift in Sources */,
|
||||||
|
973B5350B5C72E3EB1E62E67 /* ImageProviderProtocol.swift in Sources */,
|
||||||
7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */,
|
7CD16990BA843BE9ED639129 /* ImageRoomTimelineItem.swift in Sources */,
|
||||||
D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */,
|
D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */,
|
||||||
B6048166B4AA4CEFEA9B77A6 /* InfoPlistReader.swift in Sources */,
|
B6048166B4AA4CEFEA9B77A6 /* InfoPlistReader.swift in Sources */,
|
||||||
@@ -3073,6 +3097,8 @@
|
|||||||
1FE593ECEC40A43789105D80 /* KeychainController.swift in Sources */,
|
1FE593ECEC40A43789105D80 /* KeychainController.swift in Sources */,
|
||||||
CB99B0FA38A4AC596F38CC13 /* KeychainControllerProtocol.swift in Sources */,
|
CB99B0FA38A4AC596F38CC13 /* KeychainControllerProtocol.swift in Sources */,
|
||||||
15D867E638BFD0E5E71DB1EF /* List.swift in Sources */,
|
15D867E638BFD0E5E71DB1EF /* List.swift in Sources */,
|
||||||
|
6E47D126DD7585E8F8237CE7 /* LoadableAvatarImage.swift in Sources */,
|
||||||
|
D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */,
|
||||||
83E5054739949181CA981193 /* LoginCoordinator.swift in Sources */,
|
83E5054739949181CA981193 /* LoginCoordinator.swift in Sources */,
|
||||||
872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */,
|
872A6457DF573AF8CEAE927A /* LoginHomeserver.swift in Sources */,
|
||||||
CEB8FB1269DE20536608B957 /* LoginMode.swift in Sources */,
|
CEB8FB1269DE20536608B957 /* LoginMode.swift in Sources */,
|
||||||
@@ -3084,10 +3110,10 @@
|
|||||||
B94368839BDB69172E28E245 /* MXLog.swift in Sources */,
|
B94368839BDB69172E28E245 /* MXLog.swift in Sources */,
|
||||||
B66757D0254843162595B25D /* MXLogger.swift in Sources */,
|
B66757D0254843162595B25D /* MXLogger.swift in Sources */,
|
||||||
67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */,
|
67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */,
|
||||||
|
7313970ED46B213AF4CFB4B3 /* MediaLoader.swift in Sources */,
|
||||||
|
709B1C6026A3056662FF93EE /* MediaLoaderProtocol.swift in Sources */,
|
||||||
EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */,
|
EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */,
|
||||||
7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */,
|
7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */,
|
||||||
FBCD77D557AACBE9B445133A /* MediaProxy.swift in Sources */,
|
|
||||||
323F36D880363C473B81A9EA /* MediaProxyProtocol.swift in Sources */,
|
|
||||||
43BD17BC8794BB9B04F2A26B /* MediaSourceProxy.swift in Sources */,
|
43BD17BC8794BB9B04F2A26B /* MediaSourceProxy.swift in Sources */,
|
||||||
24906A1E82D0046655958536 /* MessageComposer.swift in Sources */,
|
24906A1E82D0046655958536 /* MessageComposer.swift in Sources */,
|
||||||
072BA9DBA932374CCA300125 /* MessageComposerTextField.swift in Sources */,
|
072BA9DBA932374CCA300125 /* MessageComposerTextField.swift in Sources */,
|
||||||
@@ -3132,7 +3158,7 @@
|
|||||||
7501442D52A65F73DF79FFD4 /* PaginationIndicatorRoomTimelineItem.swift in Sources */,
|
7501442D52A65F73DF79FFD4 /* PaginationIndicatorRoomTimelineItem.swift in Sources */,
|
||||||
764AFCC225B044CF5F9B41E5 /* PaginationIndicatorRoomTimelineView.swift in Sources */,
|
764AFCC225B044CF5F9B41E5 /* PaginationIndicatorRoomTimelineView.swift in Sources */,
|
||||||
80D00A7C62AAB44F54725C43 /* PermalinkBuilder.swift in Sources */,
|
80D00A7C62AAB44F54725C43 /* PermalinkBuilder.swift in Sources */,
|
||||||
7D1DAAA364A9A29D554BD24E /* PlaceholderAvatarImage.swift in Sources */,
|
9D79B94493FB32249F7E472F /* PlaceholderAvatarImage.swift in Sources */,
|
||||||
DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */,
|
DF504B10A4918F971A57BEF2 /* PostHogAnalyticsClient.swift in Sources */,
|
||||||
743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */,
|
743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */,
|
||||||
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */,
|
8EF63DDDC1B54F122070B04D /* ReadMarkerRoomTimelineView.swift in Sources */,
|
||||||
@@ -3199,11 +3225,11 @@
|
|||||||
9E8AE387FD03E4F1C1B8815A /* SessionVerificationStateMachine.swift in Sources */,
|
9E8AE387FD03E4F1C1B8815A /* SessionVerificationStateMachine.swift in Sources */,
|
||||||
A8EC7C9D886244DAE9433E37 /* SessionVerificationViewModel.swift in Sources */,
|
A8EC7C9D886244DAE9433E37 /* SessionVerificationViewModel.swift in Sources */,
|
||||||
D6417E5A799C3C7F14F9EC0A /* SessionVerificationViewModelProtocol.swift in Sources */,
|
D6417E5A799C3C7F14F9EC0A /* SessionVerificationViewModelProtocol.swift in Sources */,
|
||||||
34966D4C1C2C6D37FE3F7F50 /* SettingsCoordinator.swift in Sources */,
|
|
||||||
3B770CB4DED51CC362C66D47 /* SettingsModels.swift in Sources */,
|
|
||||||
7FED310F6AB7A70CBFB7C8A3 /* SettingsScreen.swift in Sources */,
|
7FED310F6AB7A70CBFB7C8A3 /* SettingsScreen.swift in Sources */,
|
||||||
4A2E0DBB63919AC8309B6D40 /* SettingsViewModel.swift in Sources */,
|
233221E32DA045018D3D3050 /* SettingsScreenCoordinator.swift in Sources */,
|
||||||
438FB9BC535BC95948AA5F34 /* SettingsViewModelProtocol.swift in Sources */,
|
DBAA69CC2CE4D44BC8E20105 /* SettingsScreenModels.swift in Sources */,
|
||||||
|
EEC499F9AC7DD6D18760F81D /* SettingsScreenViewModel.swift in Sources */,
|
||||||
|
50C59870BEB1F29C60252FD4 /* SettingsScreenViewModelProtocol.swift in Sources */,
|
||||||
6E6E0AAF6C44C0B117EBBE5A /* SlidingSyncViewProxy.swift in Sources */,
|
6E6E0AAF6C44C0B117EBBE5A /* SlidingSyncViewProxy.swift in Sources */,
|
||||||
2276870A19F34B3FFFDA690F /* SoftLogoutCoordinator.swift in Sources */,
|
2276870A19F34B3FFFDA690F /* SoftLogoutCoordinator.swift in Sources */,
|
||||||
214C6B416609E58CCBF6DCEE /* SoftLogoutModels.swift in Sources */,
|
214C6B416609E58CCBF6DCEE /* SoftLogoutModels.swift in Sources */,
|
||||||
@@ -3294,7 +3320,7 @@
|
|||||||
2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */,
|
2F1CF90A3460C153154427F0 /* RoomScreenUITests.swift in Sources */,
|
||||||
77FACC29F98FE2E65BBB6A5F /* ServerSelectionUITests.swift in Sources */,
|
77FACC29F98FE2E65BBB6A5F /* ServerSelectionUITests.swift in Sources */,
|
||||||
05EC896A4B9AF4A56670C0BB /* SessionVerificationUITests.swift in Sources */,
|
05EC896A4B9AF4A56670C0BB /* SessionVerificationUITests.swift in Sources */,
|
||||||
490E606044B18985055FF690 /* SettingsUITests.swift in Sources */,
|
9C5A07E7C33F3F40287D7861 /* SettingsScreenUITests.swift in Sources */,
|
||||||
B064D42BA087649ACAE462E8 /* SoftLogoutUITests.swift in Sources */,
|
B064D42BA087649ACAE462E8 /* SoftLogoutUITests.swift in Sources */,
|
||||||
DD9B70DE54B24E0694A35D8A /* Strings+Untranslated.swift in Sources */,
|
DD9B70DE54B24E0694A35D8A /* Strings+Untranslated.swift in Sources */,
|
||||||
B3357B00F1AA930E54F76609 /* Strings.swift in Sources */,
|
B3357B00F1AA930E54F76609 /* Strings.swift in Sources */,
|
||||||
|
|||||||
@@ -86,8 +86,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
|
"location" : "https://github.com/matrix-org/matrix-rust-components-swift",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "2d702d0d52805e4f81924507b39ad81c8a74a63f",
|
"revision" : "a119a8f16bbe3adc34cfdf2e092886d44e01705a",
|
||||||
"version" : "1.0.33-alpha"
|
"version" : "1.0.32-alpha"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,11 +25,7 @@ class AppCoordinator: AppCoordinatorProtocol {
|
|||||||
/// Common background task to resume long-running tasks in the background.
|
/// Common background task to resume long-running tasks in the background.
|
||||||
/// When this task expiring, we'll try to suspend the state machine by `suspend` event.
|
/// When this task expiring, we'll try to suspend the state machine by `suspend` event.
|
||||||
private var backgroundTask: BackgroundTaskProtocol?
|
private var backgroundTask: BackgroundTaskProtocol?
|
||||||
private var isSuspended = false {
|
private var isSuspended = false
|
||||||
didSet {
|
|
||||||
MXLog.info("didSet to: \(isSuspended)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var userSession: UserSessionProtocol! {
|
private var userSession: UserSessionProtocol! {
|
||||||
didSet {
|
didSet {
|
||||||
|
|||||||
@@ -37,15 +37,20 @@ class ViewModelContext<ViewState: BindableState, ViewAction>: ObservableObject {
|
|||||||
/// Get-able/Observable `Published` property for the `ViewState`
|
/// Get-able/Observable `Published` property for the `ViewState`
|
||||||
@Published fileprivate(set) var viewState: ViewState
|
@Published fileprivate(set) var viewState: ViewState
|
||||||
|
|
||||||
|
/// An optional image loading service so that views can manage themselves
|
||||||
|
/// Intentionally non-generic so that it doesn't grow uncontrollably
|
||||||
|
let imageProvider: ImageProviderProtocol?
|
||||||
|
|
||||||
/// Set-able/Bindable access to the bindable state.
|
/// Set-able/Bindable access to the bindable state.
|
||||||
subscript<T>(dynamicMember keyPath: WritableKeyPath<ViewState.BindStateType, T>) -> T {
|
subscript<T>(dynamicMember keyPath: WritableKeyPath<ViewState.BindStateType, T>) -> T {
|
||||||
get { viewState.bindings[keyPath: keyPath] }
|
get { viewState.bindings[keyPath: keyPath] }
|
||||||
set { viewState.bindings[keyPath: keyPath] = newValue }
|
set { viewState.bindings[keyPath: keyPath] = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
init(initialViewState: ViewState) {
|
init(initialViewState: ViewState, imageProvider: ImageProviderProtocol?) {
|
||||||
self.viewActions = PassthroughSubject()
|
self.viewActions = PassthroughSubject()
|
||||||
self.viewState = initialViewState
|
self.viewState = initialViewState
|
||||||
|
self.imageProvider = imageProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a `ViewAction` to the `ViewModel` for processing.
|
/// Send a `ViewAction` to the `ViewModel` for processing.
|
||||||
@@ -78,8 +83,8 @@ class StateStoreViewModel<State: BindableState, ViewAction> {
|
|||||||
set { context.viewState = newValue }
|
set { context.viewState = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
init(initialViewState: State) {
|
init(initialViewState: State, imageProvider: ImageProviderProtocol? = nil) {
|
||||||
context = Context(initialViewState: initialViewState)
|
context = Context(initialViewState: initialViewState, imageProvider: imageProvider)
|
||||||
context.viewActions
|
context.viewActions
|
||||||
.sink { [weak self] action in
|
.sink { [weak self] action in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LoadableAvatarImage: View {
|
||||||
|
private let url: URL?
|
||||||
|
private let name: String?
|
||||||
|
private let contentID: String?
|
||||||
|
private let avatarSize: AvatarSize
|
||||||
|
private let imageProvider: ImageProviderProtocol?
|
||||||
|
|
||||||
|
@ScaledMetric private var frameSize: CGFloat
|
||||||
|
|
||||||
|
init(url: URL?, name: String?, contentID: String?, avatarSize: AvatarSize, imageProvider: ImageProviderProtocol?) {
|
||||||
|
self.url = url
|
||||||
|
self.name = name
|
||||||
|
self.contentID = contentID
|
||||||
|
self.avatarSize = avatarSize
|
||||||
|
self.imageProvider = imageProvider
|
||||||
|
|
||||||
|
_frameSize = ScaledMetric(wrappedValue: avatarSize.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
LoadableImage(url: url,
|
||||||
|
size: avatarSize.scaledSize,
|
||||||
|
imageProvider: imageProvider) { image in
|
||||||
|
image
|
||||||
|
.scaledToFill()
|
||||||
|
} placeholder: {
|
||||||
|
PlaceholderAvatarImage(name: name, contentID: contentID)
|
||||||
|
}
|
||||||
|
.frame(width: frameSize, height: frameSize)
|
||||||
|
.clipShape(Circle())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LoadableImage<TransformerView: View, PlaceholderView: View>: View {
|
||||||
|
private let mediaSource: MediaSourceProxy?
|
||||||
|
private let blurhash: String?
|
||||||
|
private let size: CGSize?
|
||||||
|
private let imageProvider: ImageProviderProtocol?
|
||||||
|
|
||||||
|
private var transformer: (Image) -> TransformerView
|
||||||
|
private let placeholder: () -> PlaceholderView
|
||||||
|
|
||||||
|
@State private var cachedImage: UIImage?
|
||||||
|
|
||||||
|
/// A SwiftUI view that automatically fetches images
|
||||||
|
/// It will try fetching the image from in-memory cache and if that's not available
|
||||||
|
/// it will fire a task to load it through the image provider
|
||||||
|
/// - Parameters:
|
||||||
|
/// - mediaSource: the source of the image
|
||||||
|
/// - blurhash: an optional blurhash
|
||||||
|
/// - transformer: entry point for configuring the resulting image view
|
||||||
|
/// - placeholder: a view to show while the image or blurhash are not available
|
||||||
|
init(mediaSource: MediaSourceProxy?,
|
||||||
|
blurhash: String? = nil,
|
||||||
|
size: CGSize? = nil,
|
||||||
|
imageProvider: ImageProviderProtocol?,
|
||||||
|
transformer: @escaping (Image) -> TransformerView = { $0 },
|
||||||
|
placeholder: @escaping () -> PlaceholderView) {
|
||||||
|
self.mediaSource = mediaSource
|
||||||
|
self.blurhash = blurhash
|
||||||
|
self.size = size
|
||||||
|
self.imageProvider = imageProvider
|
||||||
|
|
||||||
|
self.transformer = transformer
|
||||||
|
self.placeholder = placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
init(url: URL?,
|
||||||
|
blurhash: String? = nil,
|
||||||
|
size: CGSize? = nil,
|
||||||
|
imageProvider: ImageProviderProtocol?,
|
||||||
|
transformer: @escaping (Image) -> TransformerView = { $0 },
|
||||||
|
placeholder: @escaping () -> PlaceholderView) {
|
||||||
|
let mediaSource = url.map(MediaSourceProxy.init)
|
||||||
|
|
||||||
|
self.init(mediaSource: mediaSource,
|
||||||
|
blurhash: blurhash,
|
||||||
|
size: size,
|
||||||
|
imageProvider: imageProvider,
|
||||||
|
transformer: transformer,
|
||||||
|
placeholder: placeholder)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let _ = Task {
|
||||||
|
// Future improvement: Does guarding against a nil image prevent the image being updated when the URL changes?
|
||||||
|
guard image == nil, let mediaSource else { return }
|
||||||
|
|
||||||
|
if case let .success(image) = await imageProvider?.loadImageFromSource(mediaSource, size: size) {
|
||||||
|
self.cachedImage = image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZStack {
|
||||||
|
if let image {
|
||||||
|
transformer(
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
)
|
||||||
|
} else if let blurhash,
|
||||||
|
// Build a small blurhash image so that it's fast
|
||||||
|
let image = UIImage(blurHash: blurhash, size: .init(width: 10.0, height: 10.0)) {
|
||||||
|
transformer(
|
||||||
|
Image(uiImage: image)
|
||||||
|
.resizable()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
placeholder()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.animation(.elementDefault, value: image)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var image: UIImage? {
|
||||||
|
cachedImage ?? imageProvider?.imageFromSource(mediaSource, size: size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2022 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PlaceholderAvatarImage: View {
|
||||||
|
@Environment(\.redactionReasons) var redactionReasons
|
||||||
|
|
||||||
|
private let textForImage: String
|
||||||
|
private let contentID: String?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
bgColor
|
||||||
|
|
||||||
|
// This text's frame doesn't look right when redacted
|
||||||
|
if redactionReasons != .placeholder {
|
||||||
|
Text(textForImage)
|
||||||
|
.padding(4)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.font(.system(size: 200).weight(.semibold))
|
||||||
|
.minimumScaleFactor(0.001)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.aspectRatio(1, contentMode: .fill)
|
||||||
|
}
|
||||||
|
|
||||||
|
init(name: String?, contentID: String?) {
|
||||||
|
let baseName = name ?? contentID?.trimmingCharacters(in: .punctuationCharacters)
|
||||||
|
textForImage = baseName?.first?.uppercased() ?? ""
|
||||||
|
self.contentID = contentID
|
||||||
|
}
|
||||||
|
|
||||||
|
private var bgColor: Color {
|
||||||
|
if redactionReasons == .placeholder {
|
||||||
|
return .element.systemGray4
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let contentID else {
|
||||||
|
return .element.accent
|
||||||
|
}
|
||||||
|
|
||||||
|
return .element.avatarBackground(for: contentID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PlaceholderAvatarImage_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
VStack(spacing: 75) {
|
||||||
|
PlaceholderAvatarImage(name: "Xavier", contentID: "@userid1:matrix.org")
|
||||||
|
.clipShape(Circle())
|
||||||
|
.frame(width: 150, height: 100)
|
||||||
|
|
||||||
|
PlaceholderAvatarImage(name: "@*~AmazingName~*@", contentID: "@userid2:matrix.org")
|
||||||
|
.clipShape(Circle())
|
||||||
|
.frame(width: 150, height: 100)
|
||||||
|
|
||||||
|
PlaceholderAvatarImage(name: nil, contentID: "@userid3:matrix.org")
|
||||||
|
.clipShape(Circle())
|
||||||
|
.frame(width: 150, height: 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,7 +34,6 @@ enum HomeScreenViewUserMenuAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum HomeScreenViewAction {
|
enum HomeScreenViewAction {
|
||||||
case loadRoomData(roomIdentifier: String)
|
|
||||||
case selectRoom(roomIdentifier: String)
|
case selectRoom(roomIdentifier: String)
|
||||||
case userMenu(action: HomeScreenViewUserMenuAction)
|
case userMenu(action: HomeScreenViewUserMenuAction)
|
||||||
case verifySession
|
case verifySession
|
||||||
@@ -59,7 +58,7 @@ enum HomeScreenRoomListMode: CustomStringConvertible {
|
|||||||
struct HomeScreenViewState: BindableState {
|
struct HomeScreenViewState: BindableState {
|
||||||
var userID: String
|
var userID: String
|
||||||
var userDisplayName: String?
|
var userDisplayName: String?
|
||||||
var userAvatar: UIImage?
|
var userAvatarURL: URL?
|
||||||
|
|
||||||
var showSessionVerificationBanner = false
|
var showSessionVerificationBanner = false
|
||||||
|
|
||||||
@@ -96,7 +95,6 @@ struct HomeScreenViewStateBindings {
|
|||||||
|
|
||||||
struct HomeScreenRoom: Identifiable, Equatable {
|
struct HomeScreenRoom: Identifiable, Equatable {
|
||||||
private static let placeholderLastMessage = AttributedString("Last message")
|
private static let placeholderLastMessage = AttributedString("Last message")
|
||||||
private static let placeholderAvatar = UIImage(systemName: "photo")
|
|
||||||
|
|
||||||
/// The list item identifier can be a real room identifier, a custom one for invalidated entries
|
/// The list item identifier can be a real room identifier, a custom one for invalidated entries
|
||||||
/// or a completely unique one for empty items and skeletons
|
/// or a completely unique one for empty items and skeletons
|
||||||
@@ -115,8 +113,6 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
|||||||
|
|
||||||
var avatarURL: URL?
|
var avatarURL: URL?
|
||||||
|
|
||||||
var avatar: UIImage?
|
|
||||||
|
|
||||||
var isPlaceholder = false
|
var isPlaceholder = false
|
||||||
|
|
||||||
static func placeholder() -> HomeScreenRoom {
|
static func placeholder() -> HomeScreenRoom {
|
||||||
@@ -126,7 +122,6 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
|||||||
hasUnreads: false,
|
hasUnreads: false,
|
||||||
timestamp: "Now",
|
timestamp: "Now",
|
||||||
lastMessage: Self.placeholderLastMessage,
|
lastMessage: Self.placeholderLastMessage,
|
||||||
avatar: Self.placeholderAvatar,
|
|
||||||
isPlaceholder: true)
|
isPlaceholder: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
// swiftlint:disable:next function_body_length cyclomatic_complexity
|
// swiftlint:disable:next function_body_length
|
||||||
init(userSession: UserSessionProtocol, attributedStringBuilder: AttributedStringBuilderProtocol) {
|
init(userSession: UserSessionProtocol, attributedStringBuilder: AttributedStringBuilderProtocol) {
|
||||||
self.userSession = userSession
|
self.userSession = userSession
|
||||||
self.attributedStringBuilder = attributedStringBuilder
|
self.attributedStringBuilder = attributedStringBuilder
|
||||||
@@ -45,7 +45,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
visibleRoomsSummaryProvider = userSession.clientProxy.visibleRoomsSummaryProvider
|
visibleRoomsSummaryProvider = userSession.clientProxy.visibleRoomsSummaryProvider
|
||||||
allRoomsSummaryProvider = userSession.clientProxy.allRoomsSummaryProvider
|
allRoomsSummaryProvider = userSession.clientProxy.allRoomsSummaryProvider
|
||||||
|
|
||||||
super.init(initialViewState: HomeScreenViewState(userID: userSession.userID))
|
super.init(initialViewState: HomeScreenViewState(userID: userSession.userID),
|
||||||
|
imageProvider: userSession.mediaProvider)
|
||||||
|
|
||||||
userSession.callbacks
|
userSession.callbacks
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
@@ -71,9 +72,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
|
|
||||||
Task {
|
Task {
|
||||||
if case let .success(url) = await userSession.clientProxy.loadUserAvatarURL() {
|
if case let .success(url) = await userSession.clientProxy.loadUserAvatarURL() {
|
||||||
if case let .success(avatar) = await userSession.mediaProvider.loadImageFromURL(url, avatarSize: .user(on: .home)) {
|
state.userAvatarURL = url
|
||||||
state.userAvatar = avatar
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,13 +138,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|
||||||
// swiftlint:disable:next cyclomatic_complexity
|
|
||||||
override func process(viewAction: HomeScreenViewAction) async {
|
override func process(viewAction: HomeScreenViewAction) async {
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .loadRoomData(let roomIdentifier):
|
|
||||||
if state.roomListMode != .skeletons {
|
|
||||||
loadDataForRoomIdentifier(roomIdentifier)
|
|
||||||
}
|
|
||||||
case .selectRoom(let roomIdentifier):
|
case .selectRoom(let roomIdentifier):
|
||||||
callback?(.presentRoom(roomIdentifier: roomIdentifier))
|
callback?(.presentRoom(roomIdentifier: roomIdentifier))
|
||||||
case .userMenu(let action):
|
case .userMenu(let action):
|
||||||
@@ -179,26 +173,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func loadDataForRoomIdentifier(_ identifier: String) {
|
|
||||||
guard let room = roomsForIdentifiers[identifier],
|
|
||||||
room.avatar == nil,
|
|
||||||
let avatarURL = room.avatarURL else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Task {
|
|
||||||
if case let .success(image) = await userSession.mediaProvider.loadImageFromURL(avatarURL, avatarSize: .room(on: .home)) {
|
|
||||||
guard let roomIndex = state.rooms.firstIndex(where: { $0.roomId == identifier }) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var room = state.rooms[roomIndex]
|
|
||||||
room.avatar = image
|
|
||||||
state.rooms[roomIndex] = room
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This method will update all view state rooms by merging the data from both summary providers
|
/// This method will update all view state rooms by merging the data from both summary providers
|
||||||
/// If a room is empty in the visible room summary provider it will try to get it from the allRooms one
|
/// If a room is empty in the visible room summary provider it will try to get it from the allRooms one
|
||||||
/// This ensures that we show as many room details as possible without loading up timelines
|
/// This ensures that we show as many room details as possible without loading up timelines
|
||||||
@@ -256,10 +230,6 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
|||||||
room.hasUnreads = details.unreadNotificationCount > 0
|
room.hasUnreads = details.unreadNotificationCount > 0
|
||||||
room.lastMessage = details.lastMessage
|
room.lastMessage = details.lastMessage
|
||||||
|
|
||||||
if let avatarURL = details.avatarURL {
|
|
||||||
room.avatar = userSession.mediaProvider.imageFromURL(avatarURL, avatarSize: .room(on: .home))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let lastMessageTimestamp = details.lastMessageTimestamp {
|
if let lastMessageTimestamp = details.lastMessageTimestamp {
|
||||||
room.timestamp = lastMessageTimestamp.formattedMinimal()
|
room.timestamp = lastMessageTimestamp.formattedMinimal()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,9 +102,12 @@ struct HomeScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
userAvatarImageView
|
LoadableAvatarImage(url: context.viewState.userAvatarURL,
|
||||||
.animation(.elementDefault, value: context.viewState.userAvatar)
|
name: context.viewState.userDisplayName,
|
||||||
.transition(.opacity)
|
contentID: context.viewState.userID,
|
||||||
|
avatarSize: .user(on: .home),
|
||||||
|
imageProvider: context.imageProvider)
|
||||||
|
.accessibilityIdentifier("userAvatarImage")
|
||||||
}
|
}
|
||||||
.alert(ElementL10n.actionSignOut,
|
.alert(ElementL10n.actionSignOut,
|
||||||
isPresented: $showingLogoutConfirmation) {
|
isPresented: $showingLogoutConfirmation) {
|
||||||
@@ -117,26 +120,6 @@ struct HomeScreen: View {
|
|||||||
.accessibilityLabel(ElementL10n.a11yAllChatsUserAvatarMenu)
|
.accessibilityLabel(ElementL10n.a11yAllChatsUserAvatarMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var userAvatarImageView: some View {
|
|
||||||
userAvatarImage
|
|
||||||
.frame(width: AvatarSize.user(on: .home).value, height: AvatarSize.user(on: .home).value, alignment: .center)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.accessibilityIdentifier("userAvatarImage")
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var userAvatarImage: some View {
|
|
||||||
if let avatar = context.viewState.userAvatar {
|
|
||||||
Image(uiImage: avatar)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
} else {
|
|
||||||
PlaceholderAvatarImage(text: context.viewState.userDisplayName ?? context.viewState.userID,
|
|
||||||
contentId: context.viewState.userID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var sessionVerificationBanner: some View {
|
private var sessionVerificationBanner: some View {
|
||||||
VStack(alignment: .leading, spacing: 16) {
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
VStack(alignment: .leading, spacing: 2) {
|
VStack(alignment: .leading, spacing: 2) {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct HomeScreenRoomCell: View {
|
struct HomeScreenRoomCell: View {
|
||||||
@ScaledMetric private var avatarSize = AvatarSize.room(on: .home).value
|
|
||||||
|
|
||||||
let room: HomeScreenRoom
|
let room: HomeScreenRoom
|
||||||
let context: HomeScreenViewModel.Context
|
let context: HomeScreenViewModel.Context
|
||||||
|
|
||||||
@@ -38,11 +36,6 @@ struct HomeScreenRoomCell: View {
|
|||||||
}
|
}
|
||||||
.frame(minHeight: 64.0)
|
.frame(minHeight: 64.0)
|
||||||
.accessibilityElement(children: .combine)
|
.accessibilityElement(children: .combine)
|
||||||
.task {
|
|
||||||
if let roomId = room.roomId {
|
|
||||||
context.send(viewAction: .loadRoomData(roomIdentifier: roomId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.buttonStyle(HomeScreenRoomCellButtonStyle())
|
.buttonStyle(HomeScreenRoomCellButtonStyle())
|
||||||
.accessibilityIdentifier("roomName:\(room.name)")
|
.accessibilityIdentifier("roomName:\(room.name)")
|
||||||
@@ -50,19 +43,12 @@ struct HomeScreenRoomCell: View {
|
|||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
var avatar: some View {
|
var avatar: some View {
|
||||||
if let avatar = room.avatar {
|
LoadableAvatarImage(url: room.avatarURL,
|
||||||
Image(uiImage: avatar)
|
name: room.name,
|
||||||
.resizable()
|
contentID: room.roomId,
|
||||||
.scaledToFill()
|
avatarSize: .room(on: .home),
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
imageProvider: context.imageProvider)
|
||||||
.clipShape(Circle())
|
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
} else {
|
|
||||||
PlaceholderAvatarImage(text: room.name, contentId: room.roomId)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.accessibilityHidden(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ struct RoomDetailsViewState: BindableState {
|
|||||||
let roomId: String
|
let roomId: String
|
||||||
let isEncrypted: Bool
|
let isEncrypted: Bool
|
||||||
let isDirect: Bool
|
let isDirect: Bool
|
||||||
var roomTitle = ""
|
var title = ""
|
||||||
var roomTopic: String?
|
var topic: String?
|
||||||
var roomAvatar: UIImage?
|
var avatarURL: URL?
|
||||||
var members: [RoomDetailsMember]
|
var members: [RoomDetailsMember]
|
||||||
|
|
||||||
var isLoadingMembers: Bool {
|
var isLoadingMembers: Bool {
|
||||||
@@ -62,8 +62,6 @@ struct RoomDetailsMember: Identifiable, Equatable {
|
|||||||
let id: String
|
let id: String
|
||||||
let name: String?
|
let name: String?
|
||||||
let avatarURL: URL?
|
let avatarURL: URL?
|
||||||
// cached
|
|
||||||
var avatar: UIImage?
|
|
||||||
|
|
||||||
init(withProxy proxy: RoomMemberProxy) {
|
init(withProxy proxy: RoomMemberProxy) {
|
||||||
id = proxy.userId
|
id = proxy.userId
|
||||||
|
|||||||
@@ -19,9 +19,6 @@ import SwiftUI
|
|||||||
typealias RoomDetailsViewModelType = StateStoreViewModel<RoomDetailsViewState, RoomDetailsViewAction>
|
typealias RoomDetailsViewModelType = StateStoreViewModel<RoomDetailsViewState, RoomDetailsViewAction>
|
||||||
|
|
||||||
class RoomDetailsViewModel: RoomDetailsViewModelType, RoomDetailsViewModelProtocol {
|
class RoomDetailsViewModel: RoomDetailsViewModelType, RoomDetailsViewModelProtocol {
|
||||||
private let roomProxy: RoomProxyProtocol
|
|
||||||
private let mediaProvider: MediaProviderProtocol
|
|
||||||
|
|
||||||
private var members: [RoomMemberProxy] = [] {
|
private var members: [RoomMemberProxy] = [] {
|
||||||
didSet {
|
didSet {
|
||||||
state.members = members.map { RoomDetailsMember(withProxy: $0) }
|
state.members = members.map { RoomDetailsMember(withProxy: $0) }
|
||||||
@@ -30,17 +27,16 @@ class RoomDetailsViewModel: RoomDetailsViewModelType, RoomDetailsViewModelProtoc
|
|||||||
|
|
||||||
var callback: ((RoomDetailsViewModelAction) -> Void)?
|
var callback: ((RoomDetailsViewModelAction) -> Void)?
|
||||||
|
|
||||||
init(roomProxy: RoomProxyProtocol,
|
init(roomProxy: RoomProxyProtocol, mediaProvider: MediaProviderProtocol) {
|
||||||
mediaProvider: MediaProviderProtocol) {
|
|
||||||
self.roomProxy = roomProxy
|
|
||||||
self.mediaProvider = mediaProvider
|
|
||||||
super.init(initialViewState: .init(roomId: roomProxy.id,
|
super.init(initialViewState: .init(roomId: roomProxy.id,
|
||||||
isEncrypted: roomProxy.isEncrypted,
|
isEncrypted: roomProxy.isEncrypted,
|
||||||
isDirect: roomProxy.isDirect,
|
isDirect: roomProxy.isDirect,
|
||||||
roomTitle: roomProxy.displayName ?? roomProxy.name ?? "Unknown Room",
|
title: roomProxy.displayName ?? roomProxy.name ?? "Unknown Room",
|
||||||
roomTopic: roomProxy.topic,
|
topic: roomProxy.topic,
|
||||||
|
avatarURL: roomProxy.avatarURL,
|
||||||
members: [],
|
members: [],
|
||||||
bindings: .init()))
|
bindings: .init()),
|
||||||
|
imageProvider: mediaProvider)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
switch await roomProxy.members() {
|
switch await roomProxy.members() {
|
||||||
@@ -51,14 +47,6 @@ class RoomDetailsViewModel: RoomDetailsViewModelType, RoomDetailsViewModelProtoc
|
|||||||
state.bindings.alertInfo = AlertInfo(id: .alert(ElementL10n.unknownError))
|
state.bindings.alertInfo = AlertInfo(id: .alert(ElementL10n.unknownError))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let avatarURL = roomProxy.avatarURL {
|
|
||||||
Task {
|
|
||||||
if case let .success(avatar) = await mediaProvider.loadImageFromURL(avatarURL, avatarSize: .room(on: .details)) {
|
|
||||||
state.roomAvatar = avatar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|||||||
@@ -17,24 +17,16 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct RoomDetailsScreen: View {
|
struct RoomDetailsScreen: View {
|
||||||
// MARK: Private
|
|
||||||
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
|
||||||
@ScaledMetric private var avatarSize = AvatarSize.room(on: .details).value
|
|
||||||
@ScaledMetric private var menuIconSize = 30.0
|
@ScaledMetric private var menuIconSize = 30.0
|
||||||
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
|
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
|
||||||
|
|
||||||
// MARK: Public
|
|
||||||
|
|
||||||
@ObservedObject var context: RoomDetailsViewModel.Context
|
@ObservedObject var context: RoomDetailsViewModel.Context
|
||||||
|
|
||||||
// MARK: Views
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
headerSection
|
headerSection
|
||||||
|
|
||||||
if let topic = context.viewState.roomTopic {
|
if let topic = context.viewState.topic {
|
||||||
topicSection(with: topic)
|
topicSection(with: topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,10 +40,18 @@ struct RoomDetailsScreen: View {
|
|||||||
.navigationTitle(ElementL10n.roomDetailsTitle)
|
.navigationTitle(ElementL10n.roomDetailsTitle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
private var headerSection: some View {
|
private var headerSection: some View {
|
||||||
VStack(spacing: 16.0) {
|
VStack(spacing: 16.0) {
|
||||||
roomAvatarImage
|
LoadableAvatarImage(url: context.viewState.avatarURL,
|
||||||
Text(context.viewState.roomTitle)
|
name: context.viewState.title,
|
||||||
|
contentID: context.viewState.roomId,
|
||||||
|
avatarSize: .room(on: .details),
|
||||||
|
imageProvider: context.imageProvider)
|
||||||
|
.accessibilityIdentifier("roomAvatarImage")
|
||||||
|
|
||||||
|
Text(context.viewState.title)
|
||||||
.foregroundColor(.element.primaryContent)
|
.foregroundColor(.element.primaryContent)
|
||||||
.font(.element.headline)
|
.font(.element.headline)
|
||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
@@ -128,23 +128,6 @@ struct RoomDetailsScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private var roomAvatarImage: some View {
|
|
||||||
if let avatar = context.viewState.roomAvatar {
|
|
||||||
Image(uiImage: avatar)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.accessibilityIdentifier("roomAvatarImage")
|
|
||||||
} else {
|
|
||||||
PlaceholderAvatarImage(text: context.viewState.roomTitle,
|
|
||||||
contentId: context.viewState.roomId)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.accessibilityIdentifier("roomAvatarPlaceholderImage")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|||||||
@@ -46,5 +46,4 @@ struct RoomMemberDetailsViewStateBindings {
|
|||||||
|
|
||||||
enum RoomMemberDetailsViewAction {
|
enum RoomMemberDetailsViewAction {
|
||||||
case selectMember(id: String)
|
case selectMember(id: String)
|
||||||
case loadMemberData(id: String)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDeta
|
|||||||
members: [RoomMemberProxy]) {
|
members: [RoomMemberProxy]) {
|
||||||
self.mediaProvider = mediaProvider
|
self.mediaProvider = mediaProvider
|
||||||
super.init(initialViewState: .init(members: members.map { RoomDetailsMember(withProxy: $0) },
|
super.init(initialViewState: .init(members: members.map { RoomDetailsMember(withProxy: $0) },
|
||||||
bindings: .init()))
|
bindings: .init()),
|
||||||
|
imageProvider: mediaProvider)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
@@ -36,30 +37,6 @@ class RoomMemberDetailsViewModel: RoomMemberDetailsViewModelType, RoomMemberDeta
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .selectMember(let id):
|
case .selectMember(let id):
|
||||||
MXLog.debug("Member selected: \(id)")
|
MXLog.debug("Member selected: \(id)")
|
||||||
case .loadMemberData(let id):
|
|
||||||
await loadAvatar(forMember: id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadAvatar(forMember memberId: String) async {
|
|
||||||
guard let member = state.members.first(where: { $0.id == memberId }) else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if member.avatar != nil {
|
|
||||||
// Avatar already loaded.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let avatarURL = member.avatarURL else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch await mediaProvider.loadImageFromURL(avatarURL, avatarSize: .user(on: .roomDetails)) {
|
|
||||||
case .success(let image):
|
|
||||||
if let index = state.members.firstIndex(where: { $0.id == memberId }) {
|
|
||||||
state.members[index].avatar = image
|
|
||||||
}
|
|
||||||
case .failure(let error):
|
|
||||||
MXLog.error("Failed to retrieve room member avatar: \(error)")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,19 +27,12 @@ struct RoomMemberDetailsMemberCell: View {
|
|||||||
context.send(viewAction: .selectMember(id: member.id))
|
context.send(viewAction: .selectMember(id: member.id))
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
if let avatar = member.avatar {
|
LoadableAvatarImage(url: member.avatarURL,
|
||||||
Image(uiImage: avatar)
|
name: member.name ?? "",
|
||||||
.resizable()
|
contentID: member.id,
|
||||||
.scaledToFill()
|
avatarSize: .user(on: .roomDetails),
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
imageProvider: context.imageProvider)
|
||||||
.clipShape(Circle())
|
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
} else {
|
|
||||||
PlaceholderAvatarImage(text: member.name ?? "", contentId: member.id)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.accessibilityHidden(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
Text(member.name ?? "")
|
Text(member.name ?? "")
|
||||||
.font(.element.callout.bold())
|
.font(.element.callout.bold())
|
||||||
@@ -49,9 +42,6 @@ struct RoomMemberDetailsMemberCell: View {
|
|||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
.accessibilityElement(children: .combine)
|
.accessibilityElement(children: .combine)
|
||||||
.task {
|
|
||||||
context.send(viewAction: .loadMemberData(id: member.id))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ enum RoomScreenViewAction {
|
|||||||
struct RoomScreenViewState: BindableState {
|
struct RoomScreenViewState: BindableState {
|
||||||
var roomId: String
|
var roomId: String
|
||||||
var roomTitle = ""
|
var roomTitle = ""
|
||||||
var roomAvatar: UIImage?
|
var roomAvatarURL: URL?
|
||||||
var items: [RoomTimelineViewProvider] = []
|
var items: [RoomTimelineViewProvider] = []
|
||||||
var canBackPaginate = true
|
var canBackPaginate = true
|
||||||
var isBackPaginating = false
|
var isBackPaginating = false
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
|
|
||||||
private let timelineController: RoomTimelineControllerProtocol
|
private let timelineController: RoomTimelineControllerProtocol
|
||||||
private let timelineViewFactory: RoomTimelineViewFactoryProtocol
|
private let timelineViewFactory: RoomTimelineViewFactoryProtocol
|
||||||
private let mediaProvider: MediaProviderProtocol
|
|
||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
@@ -39,12 +38,12 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
roomAvatarUrl: URL? = nil) {
|
roomAvatarUrl: URL? = nil) {
|
||||||
self.timelineController = timelineController
|
self.timelineController = timelineController
|
||||||
self.timelineViewFactory = timelineViewFactory
|
self.timelineViewFactory = timelineViewFactory
|
||||||
self.mediaProvider = mediaProvider
|
|
||||||
|
|
||||||
super.init(initialViewState: RoomScreenViewState(roomId: timelineController.roomID,
|
super.init(initialViewState: RoomScreenViewState(roomId: timelineController.roomID,
|
||||||
roomTitle: roomName ?? "Unknown room 💥",
|
roomTitle: roomName ?? "Unknown room 💥",
|
||||||
roomAvatar: nil,
|
roomAvatarURL: roomAvatarUrl,
|
||||||
bindings: .init(composerText: "", composerFocused: false)))
|
bindings: .init(composerText: "", composerFocused: false)),
|
||||||
|
imageProvider: mediaProvider)
|
||||||
|
|
||||||
timelineController.callbacks
|
timelineController.callbacks
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
@@ -76,15 +75,6 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
state.contextMenuBuilder = buildContextMenuForItemId(_:)
|
state.contextMenuBuilder = buildContextMenuForItemId(_:)
|
||||||
|
|
||||||
buildTimelineViews()
|
buildTimelineViews()
|
||||||
|
|
||||||
if let roomAvatarUrl {
|
|
||||||
Task {
|
|
||||||
if case let .success(avatar) = await mediaProvider.loadImageFromURL(roomAvatarUrl,
|
|
||||||
avatarSize: .room(on: .timeline)) {
|
|
||||||
state.roomAvatar = avatar
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public
|
// MARK: - Public
|
||||||
|
|||||||
@@ -38,24 +38,12 @@ struct RoomHeaderView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private var roomAvatar: some View {
|
@ViewBuilder private var roomAvatar: some View {
|
||||||
ZStack(alignment: .bottomTrailing) {
|
LoadableAvatarImage(url: context.viewState.roomAvatarURL,
|
||||||
roomAvatarImage
|
name: context.viewState.roomTitle,
|
||||||
.clipShape(Circle())
|
contentID: context.viewState.roomId,
|
||||||
}
|
avatarSize: .room(on: .timeline),
|
||||||
.frame(width: AvatarSize.room(on: .timeline).value, height: AvatarSize.room(on: .timeline).value)
|
imageProvider: context.imageProvider)
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder private var roomAvatarImage: some View {
|
|
||||||
if let avatar = context.viewState.roomAvatar {
|
|
||||||
Image(uiImage: avatar)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.accessibilityIdentifier("roomAvatarImage")
|
.accessibilityIdentifier("roomAvatarImage")
|
||||||
} else {
|
|
||||||
PlaceholderAvatarImage(text: context.viewState.roomTitle,
|
|
||||||
contentId: context.viewState.roomId)
|
|
||||||
.accessibilityIdentifier("roomAvatarPlaceholderImage")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,21 +18,21 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct ImageRoomTimelineView: View {
|
struct ImageRoomTimelineView: View {
|
||||||
|
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||||
let timelineItem: ImageRoomTimelineItem
|
let timelineItem: ImageRoomTimelineItem
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TimelineStyler(timelineItem: timelineItem) {
|
TimelineStyler(timelineItem: timelineItem) {
|
||||||
if let image = timelineItem.image {
|
LoadableImage(mediaSource: timelineItem.source,
|
||||||
Image(uiImage: image)
|
blurhash: timelineItem.blurhash,
|
||||||
.resizable()
|
imageProvider: context.imageProvider) {
|
||||||
|
placeholder
|
||||||
|
}
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
||||||
} else if let blurhash = timelineItem.blurhash,
|
}
|
||||||
// Build a small blurhash image so that it's fast
|
}
|
||||||
let image = UIImage(blurHash: blurhash, size: .init(width: 10.0, height: 10.0)) {
|
|
||||||
Image(uiImage: image)
|
var placeholder: some View {
|
||||||
.resizable()
|
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
|
||||||
} else {
|
|
||||||
ZStack {
|
ZStack {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(.element.systemGray6)
|
.foregroundColor(.element.systemGray6)
|
||||||
@@ -41,10 +41,6 @@ struct ImageRoomTimelineView: View {
|
|||||||
ProgressView(ElementL10n.loading)
|
ProgressView(ElementL10n.loading)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animation(.elementDefault, value: timelineItem.image)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +59,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
|||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
source: nil,
|
source: nil))
|
||||||
image: UIImage(systemName: "photo")))
|
|
||||||
|
|
||||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
||||||
text: "Some other image",
|
text: "Some other image",
|
||||||
@@ -73,8 +68,7 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
|||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
source: nil,
|
source: nil))
|
||||||
image: nil))
|
|
||||||
|
|
||||||
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
ImageRoomTimelineView(timelineItem: ImageRoomTimelineItem(id: UUID().uuidString,
|
||||||
text: "Blurhashed image",
|
text: "Blurhashed image",
|
||||||
@@ -84,7 +78,6 @@ struct ImageRoomTimelineView_Previews: PreviewProvider {
|
|||||||
isEditable: false,
|
isEditable: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
source: nil,
|
source: nil,
|
||||||
image: nil,
|
|
||||||
aspectRatio: 0.7,
|
aspectRatio: 0.7,
|
||||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2022 New Vector Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct PlaceholderAvatarImage: View {
|
|
||||||
private let textForImage: String
|
|
||||||
private let contentId: String?
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
bgColor
|
|
||||||
Text(textForImage)
|
|
||||||
.padding(4)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.font(.system(size: 200).weight(.semibold))
|
|
||||||
.minimumScaleFactor(0.001)
|
|
||||||
}
|
|
||||||
.aspectRatio(1, contentMode: .fill)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(text: String, contentId: String? = nil) {
|
|
||||||
textForImage = text.first?.uppercased() ?? ""
|
|
||||||
self.contentId = contentId
|
|
||||||
}
|
|
||||||
|
|
||||||
private var bgColor: Color {
|
|
||||||
guard let contentId else {
|
|
||||||
return .element.accent
|
|
||||||
}
|
|
||||||
|
|
||||||
return .element.avatarBackground(for: contentId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PlaceholderAvatarImage_Previews: PreviewProvider {
|
|
||||||
static var previews: some View {
|
|
||||||
PlaceholderAvatarImage(text: "X", contentId: "@userid:matrix.org")
|
|
||||||
.clipShape(Circle())
|
|
||||||
.frame(width: 150, height: 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,21 +18,22 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct StickerRoomTimelineView: View {
|
struct StickerRoomTimelineView: View {
|
||||||
|
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||||
let timelineItem: StickerRoomTimelineItem
|
let timelineItem: StickerRoomTimelineItem
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TimelineStyler(timelineItem: timelineItem) {
|
TimelineStyler(timelineItem: timelineItem) {
|
||||||
if let image = timelineItem.image {
|
LoadableImage(url: timelineItem.imageURL,
|
||||||
Image(uiImage: image)
|
blurhash: timelineItem.blurhash,
|
||||||
.resizable()
|
imageProvider: context.imageProvider) {
|
||||||
|
placeholder
|
||||||
|
}
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
||||||
} else if let blurhash = timelineItem.blurhash,
|
}
|
||||||
// Build a small blurhash image so that it's fast
|
.accessibilityLabel(timelineItem.text)
|
||||||
let image = UIImage(blurHash: blurhash, size: .init(width: 10.0, height: 10.0)) {
|
}
|
||||||
Image(uiImage: image)
|
|
||||||
.resizable()
|
private var placeholder: some View {
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
|
||||||
} else {
|
|
||||||
ZStack {
|
ZStack {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(.element.systemGray6)
|
.foregroundColor(.element.systemGray6)
|
||||||
@@ -41,11 +42,6 @@ struct StickerRoomTimelineView: View {
|
|||||||
ProgressView(ElementL10n.loading)
|
ProgressView(ElementL10n.loading)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animation(.elementDefault, value: timelineItem.image)
|
|
||||||
.accessibilityLabel(timelineItem.text)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +60,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider {
|
|||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
imageURL: nil,
|
imageURL: URL.picturesDirectory))
|
||||||
image: UIImage(systemName: "photo")))
|
|
||||||
|
|
||||||
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: UUID().uuidString,
|
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: UUID().uuidString,
|
||||||
text: "Some other image",
|
text: "Some other image",
|
||||||
@@ -74,8 +69,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider {
|
|||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
imageURL: nil,
|
imageURL: URL.picturesDirectory))
|
||||||
image: nil))
|
|
||||||
|
|
||||||
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: UUID().uuidString,
|
StickerRoomTimelineView(timelineItem: StickerRoomTimelineItem(id: UUID().uuidString,
|
||||||
text: "Blurhashed image",
|
text: "Blurhashed image",
|
||||||
@@ -84,8 +78,7 @@ struct StickerRoomTimelineView_Previews: PreviewProvider {
|
|||||||
isOutgoing: false,
|
isOutgoing: false,
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
imageURL: nil,
|
imageURL: URL.picturesDirectory,
|
||||||
image: nil,
|
|
||||||
aspectRatio: 0.7,
|
aspectRatio: 0.7,
|
||||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,17 +18,32 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct VideoRoomTimelineView: View {
|
struct VideoRoomTimelineView: View {
|
||||||
|
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||||
let timelineItem: VideoRoomTimelineItem
|
let timelineItem: VideoRoomTimelineItem
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
TimelineStyler(timelineItem: timelineItem) {
|
TimelineStyler(timelineItem: timelineItem) {
|
||||||
if let image = timelineItem.image {
|
LoadableImage(mediaSource: timelineItem.thumbnailSource,
|
||||||
thumbnail(with: image)
|
blurhash: timelineItem.blurhash,
|
||||||
} else if let blurhash = timelineItem.blurhash,
|
imageProvider: context.imageProvider) { imageView in
|
||||||
// Build a small blurhash image so that it's fast
|
imageView
|
||||||
let image = UIImage(blurHash: blurhash, size: .init(width: 10.0, height: 10.0)) {
|
.overlay { playIcon }
|
||||||
thumbnail(with: image)
|
} placeholder: {
|
||||||
} else {
|
placeholder
|
||||||
|
}
|
||||||
|
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var playIcon: some View {
|
||||||
|
Image(systemName: "play.circle.fill")
|
||||||
|
.resizable()
|
||||||
|
.frame(width: 50, height: 50)
|
||||||
|
.background(.ultraThinMaterial, in: Circle())
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
|
||||||
|
var placeholder: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
Rectangle()
|
Rectangle()
|
||||||
.foregroundColor(.element.systemGray6)
|
.foregroundColor(.element.systemGray6)
|
||||||
@@ -37,24 +52,6 @@ struct VideoRoomTimelineView: View {
|
|||||||
ProgressView(ElementL10n.loading)
|
ProgressView(ElementL10n.loading)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
}
|
}
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.animation(.elementDefault, value: timelineItem.image)
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private func thumbnail(with image: UIImage) -> some View {
|
|
||||||
ZStack {
|
|
||||||
Image(uiImage: image)
|
|
||||||
.resizable()
|
|
||||||
.aspectRatio(timelineItem.aspectRatio, contentMode: .fit)
|
|
||||||
Image(systemName: "play.circle.fill")
|
|
||||||
.resizable()
|
|
||||||
.frame(width: 50, height: 50)
|
|
||||||
.background(.ultraThinMaterial, in: Circle())
|
|
||||||
.foregroundColor(.white)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +72,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider {
|
|||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
duration: 21,
|
duration: 21,
|
||||||
source: nil,
|
source: nil,
|
||||||
thumbnailSource: nil,
|
thumbnailSource: nil))
|
||||||
image: UIImage(systemName: "photo")))
|
|
||||||
|
|
||||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
|
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
|
||||||
text: "Some other video",
|
text: "Some other video",
|
||||||
@@ -87,8 +83,7 @@ struct VideoRoomTimelineView_Previews: PreviewProvider {
|
|||||||
sender: .init(id: "Bob"),
|
sender: .init(id: "Bob"),
|
||||||
duration: 22,
|
duration: 22,
|
||||||
source: nil,
|
source: nil,
|
||||||
thumbnailSource: nil,
|
thumbnailSource: nil))
|
||||||
image: nil))
|
|
||||||
|
|
||||||
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
|
VideoRoomTimelineView(timelineItem: VideoRoomTimelineItem(id: UUID().uuidString,
|
||||||
text: "Blurhashed video",
|
text: "Blurhashed video",
|
||||||
@@ -100,7 +95,6 @@ struct VideoRoomTimelineView_Previews: PreviewProvider {
|
|||||||
duration: 23,
|
duration: 23,
|
||||||
source: nil,
|
source: nil,
|
||||||
thumbnailSource: nil,
|
thumbnailSource: nil,
|
||||||
image: nil,
|
|
||||||
aspectRatio: 0.7,
|
aspectRatio: 0.7,
|
||||||
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
blurhash: "L%KUc%kqS$RP?Ks,WEf8OlrqaekW"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,29 +18,19 @@ import Foundation
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct TimelineSenderAvatarView: View {
|
struct TimelineSenderAvatarView: View {
|
||||||
let timelineItem: EventBasedTimelineItemProtocol
|
@EnvironmentObject private var context: RoomScreenViewModel.Context
|
||||||
|
|
||||||
@ScaledMetric private var avatarSize = AvatarSize.user(on: .timeline).value
|
@ScaledMetric private var avatarSize = AvatarSize.user(on: .timeline).value
|
||||||
|
|
||||||
var body: some View {
|
let timelineItem: EventBasedTimelineItemProtocol
|
||||||
ZStack(alignment: .center) {
|
|
||||||
if let avatar = timelineItem.sender.avatar {
|
|
||||||
Image(uiImage: avatar)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.overlay(Circle().stroke(Color.element.accent))
|
|
||||||
} else {
|
|
||||||
PlaceholderAvatarImage(text: timelineItem.sender.displayName ?? timelineItem.sender.id,
|
|
||||||
contentId: timelineItem.sender.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.clipShape(Circle())
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.overlay(
|
|
||||||
Circle()
|
|
||||||
.stroke(Color.element.background, lineWidth: 3)
|
|
||||||
)
|
|
||||||
|
|
||||||
.animation(.elementDefault, value: timelineItem.sender.avatar)
|
var body: some View {
|
||||||
|
LoadableAvatarImage(url: timelineItem.sender.avatarURL,
|
||||||
|
name: timelineItem.sender.displayName,
|
||||||
|
contentID: timelineItem.sender.id,
|
||||||
|
avatarSize: .user(on: .timeline),
|
||||||
|
imageProvider: context.imageProvider)
|
||||||
|
.overlay(
|
||||||
|
Circle().stroke(Color.element.background, lineWidth: 3)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -16,30 +16,30 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct SettingsCoordinatorParameters {
|
struct SettingsScreenCoordinatorParameters {
|
||||||
let navigationStackCoordinator: NavigationStackCoordinator
|
let navigationStackCoordinator: NavigationStackCoordinator
|
||||||
let userNotificationController: UserNotificationControllerProtocol
|
let userNotificationController: UserNotificationControllerProtocol
|
||||||
let userSession: UserSessionProtocol
|
let userSession: UserSessionProtocol
|
||||||
let bugReportService: BugReportServiceProtocol
|
let bugReportService: BugReportServiceProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SettingsCoordinatorAction {
|
enum SettingsScreenCoordinatorAction {
|
||||||
case dismiss
|
case dismiss
|
||||||
case logout
|
case logout
|
||||||
}
|
}
|
||||||
|
|
||||||
final class SettingsCoordinator: CoordinatorProtocol {
|
final class SettingsScreenCoordinator: CoordinatorProtocol {
|
||||||
private let parameters: SettingsCoordinatorParameters
|
private let parameters: SettingsScreenCoordinatorParameters
|
||||||
private var viewModel: SettingsViewModelProtocol
|
private var viewModel: SettingsScreenViewModelProtocol
|
||||||
|
|
||||||
var callback: ((SettingsCoordinatorAction) -> Void)?
|
var callback: ((SettingsScreenCoordinatorAction) -> Void)?
|
||||||
|
|
||||||
// MARK: - Setup
|
// MARK: - Setup
|
||||||
|
|
||||||
init(parameters: SettingsCoordinatorParameters) {
|
init(parameters: SettingsScreenCoordinatorParameters) {
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
|
|
||||||
viewModel = SettingsViewModel(withUserSession: parameters.userSession)
|
viewModel = SettingsScreenViewModel(withUserSession: parameters.userSession)
|
||||||
viewModel.callback = { [weak self] action in
|
viewModel.callback = { [weak self] action in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|
||||||
+6
-6
@@ -17,26 +17,26 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
enum SettingsViewModelAction {
|
enum SettingsScreenViewModelAction {
|
||||||
case close
|
case close
|
||||||
case toggleAnalytics
|
case toggleAnalytics
|
||||||
case reportBug
|
case reportBug
|
||||||
case logout
|
case logout
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsViewState: BindableState {
|
struct SettingsScreenViewState: BindableState {
|
||||||
var bindings: SettingsViewStateBindings
|
var bindings: SettingsScreenViewStateBindings
|
||||||
var deviceID: String?
|
var deviceID: String?
|
||||||
var userID: String
|
var userID: String
|
||||||
var userAvatar: UIImage?
|
var userAvatarURL: URL?
|
||||||
var userDisplayName: String?
|
var userDisplayName: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsViewStateBindings {
|
struct SettingsScreenViewStateBindings {
|
||||||
var enableAnalytics = ServiceLocator.shared.settings.enableAnalytics
|
var enableAnalytics = ServiceLocator.shared.settings.enableAnalytics
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SettingsViewAction {
|
enum SettingsScreenViewAction {
|
||||||
case close
|
case close
|
||||||
case toggleAnalytics
|
case toggleAnalytics
|
||||||
case reportBug
|
case reportBug
|
||||||
+8
-9
@@ -16,23 +16,22 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
typealias SettingsViewModelType = StateStoreViewModel<SettingsViewState, SettingsViewAction>
|
typealias SettingsScreenViewModelType = StateStoreViewModel<SettingsScreenViewState, SettingsScreenViewAction>
|
||||||
|
|
||||||
class SettingsViewModel: SettingsViewModelType, SettingsViewModelProtocol {
|
class SettingsScreenViewModel: SettingsScreenViewModelType, SettingsScreenViewModelProtocol {
|
||||||
private let userSession: UserSessionProtocol
|
private let userSession: UserSessionProtocol
|
||||||
|
|
||||||
var callback: ((SettingsViewModelAction) -> Void)?
|
var callback: ((SettingsScreenViewModelAction) -> Void)?
|
||||||
|
|
||||||
init(withUserSession userSession: UserSessionProtocol) {
|
init(withUserSession userSession: UserSessionProtocol) {
|
||||||
self.userSession = userSession
|
self.userSession = userSession
|
||||||
let bindings = SettingsViewStateBindings()
|
let bindings = SettingsScreenViewStateBindings()
|
||||||
super.init(initialViewState: .init(bindings: bindings, deviceID: userSession.deviceId, userID: userSession.userID))
|
super.init(initialViewState: .init(bindings: bindings, deviceID: userSession.deviceId, userID: userSession.userID),
|
||||||
|
imageProvider: userSession.mediaProvider)
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
if case let .success(userAvatarURL) = await userSession.clientProxy.loadUserAvatarURL() {
|
if case let .success(userAvatarURL) = await userSession.clientProxy.loadUserAvatarURL() {
|
||||||
if case let .success(avatar) = await userSession.mediaProvider.loadImageFromURL(userAvatarURL, avatarSize: .user(on: .settings)) {
|
state.userAvatarURL = userAvatarURL
|
||||||
state.userAvatar = avatar
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +42,7 @@ class SettingsViewModel: SettingsViewModelType, SettingsViewModelProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func process(viewAction: SettingsViewAction) async {
|
override func process(viewAction: SettingsScreenViewAction) async {
|
||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .close:
|
case .close:
|
||||||
callback?(.close)
|
callback?(.close)
|
||||||
+3
-3
@@ -17,7 +17,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
protocol SettingsViewModelProtocol {
|
protocol SettingsScreenViewModelProtocol {
|
||||||
var callback: ((SettingsViewModelAction) -> Void)? { get set }
|
var callback: ((SettingsScreenViewModelAction) -> Void)? { get set }
|
||||||
var context: SettingsViewModelType.Context { get }
|
var context: SettingsScreenViewModelType.Context { get }
|
||||||
}
|
}
|
||||||
@@ -25,7 +25,7 @@ struct SettingsScreen: View {
|
|||||||
@ScaledMetric private var menuIconSize = 30.0
|
@ScaledMetric private var menuIconSize = 30.0
|
||||||
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
|
private let listRowInsets = EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16)
|
||||||
|
|
||||||
@ObservedObject var context: SettingsViewModel.Context
|
@ObservedObject var context: SettingsScreenViewModel.Context
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
@@ -69,7 +69,12 @@ struct SettingsScreen: View {
|
|||||||
private var userSection: some View {
|
private var userSection: some View {
|
||||||
Section {
|
Section {
|
||||||
HStack(spacing: 13) {
|
HStack(spacing: 13) {
|
||||||
userAvatar
|
LoadableAvatarImage(url: context.viewState.userAvatarURL,
|
||||||
|
name: context.viewState.userDisplayName,
|
||||||
|
contentID: context.viewState.userID,
|
||||||
|
avatarSize: .user(on: .settings),
|
||||||
|
imageProvider: context.imageProvider)
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
Text(context.viewState.userDisplayName ?? "")
|
Text(context.viewState.userDisplayName ?? "")
|
||||||
.font(.title3)
|
.font(.title3)
|
||||||
@@ -81,21 +86,6 @@ struct SettingsScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
|
||||||
private var userAvatar: some View {
|
|
||||||
if let avatar = context.viewState.userAvatar {
|
|
||||||
Image(uiImage: avatar)
|
|
||||||
.resizable()
|
|
||||||
.scaledToFill()
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
.clipShape(Circle())
|
|
||||||
} else {
|
|
||||||
PlaceholderAvatarImage(text: context.viewState.userDisplayName ?? context.viewState.userID, contentId: context.viewState.userID)
|
|
||||||
.clipShape(Circle())
|
|
||||||
.frame(width: avatarSize, height: avatarSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var analyticsSection: some View {
|
private var analyticsSection: some View {
|
||||||
Section {
|
Section {
|
||||||
Button { context.send(viewAction: .reportBug) } label: {
|
Button { context.send(viewAction: .reportBug) } label: {
|
||||||
@@ -198,11 +188,11 @@ extension TimelineStyle: CustomStringConvertible {
|
|||||||
|
|
||||||
// MARK: - Previews
|
// MARK: - Previews
|
||||||
|
|
||||||
struct Settings_Previews: PreviewProvider {
|
struct SettingsScreen_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: "@userid:example.com"),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider())
|
||||||
let viewModel = SettingsViewModel(withUserSession: userSession)
|
let viewModel = SettingsScreenViewModel(withUserSession: userSession)
|
||||||
|
|
||||||
NavigationView {
|
NavigationView {
|
||||||
SettingsScreen(context: viewModel.context)
|
SettingsScreen(context: viewModel.context)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class UIKitBackgroundTask: BackgroundTaskProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if identifier == .invalid {
|
if identifier == .invalid {
|
||||||
MXLog.info("Do not start background task: \(name), as OS declined")
|
MXLog.error("Do not start background task: \(name), as OS declined")
|
||||||
// call expiration handler immediately
|
// call expiration handler immediately
|
||||||
expirationHandler?(self)
|
expirationHandler?(self)
|
||||||
return nil
|
return nil
|
||||||
@@ -69,7 +69,7 @@ class UIKitBackgroundTask: BackgroundTaskProtocol {
|
|||||||
reuse()
|
reuse()
|
||||||
}
|
}
|
||||||
|
|
||||||
MXLog.info("Start background task #\(identifier.rawValue) - \(name)")
|
MXLog.verbose("Start background task #\(identifier.rawValue) - \(name)")
|
||||||
}
|
}
|
||||||
|
|
||||||
func reuse() {
|
func reuse() {
|
||||||
@@ -92,7 +92,7 @@ class UIKitBackgroundTask: BackgroundTaskProtocol {
|
|||||||
|
|
||||||
private func endTask() {
|
private func endTask() {
|
||||||
if identifier != .invalid {
|
if identifier != .invalid {
|
||||||
MXLog.info("End background task #\(identifier.rawValue) - \(name) after \(readableElapsedTime)")
|
MXLog.verbose("End background task #\(identifier.rawValue) - \(name) after \(readableElapsedTime)")
|
||||||
|
|
||||||
application.endBackgroundTask(identifier)
|
application.endBackgroundTask(identifier)
|
||||||
identifier = .invalid
|
identifier = .invalid
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if avoidStartingNewTasks(for: application) {
|
if avoidStartingNewTasks(for: application) {
|
||||||
MXLog.info("Do not start background task: \(name), as not enough time exists")
|
MXLog.error("Do not start background task: \(name), as not enough time exists")
|
||||||
// call expiration handler immediately
|
// call expiration handler immediately
|
||||||
expirationHandler?()
|
expirationHandler?()
|
||||||
return nil
|
return nil
|
||||||
@@ -82,7 +82,7 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol {
|
|||||||
let appState = application.applicationState
|
let appState = application.applicationState
|
||||||
let remainingTime = readableBackgroundTimeRemaining(application.backgroundTimeRemaining)
|
let remainingTime = readableBackgroundTimeRemaining(application.backgroundTimeRemaining)
|
||||||
|
|
||||||
MXLog.info("Background task \(name) \(created ? "started" : "reused") with app state: \(appState) and estimated background time remaining: \(remainingTime)")
|
MXLog.verbose("Background task \(name) \(created ? "started" : "reused") with app state: \(appState) and estimated background time remaining: \(remainingTime)")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
private let client: ClientProtocol
|
private let client: ClientProtocol
|
||||||
private let backgroundTaskService: BackgroundTaskServiceProtocol
|
private let backgroundTaskService: BackgroundTaskServiceProtocol
|
||||||
private var sessionVerificationControllerProxy: SessionVerificationControllerProxy?
|
private var sessionVerificationControllerProxy: SessionVerificationControllerProxy?
|
||||||
private let mediaProxy: MediaProxyProtocol
|
private let mediaLoader: MediaLoaderProtocol
|
||||||
private let clientQueue: DispatchQueue
|
private let clientQueue: DispatchQueue
|
||||||
|
|
||||||
private var slidingSyncObserverToken: StoppableSpawn?
|
private var slidingSyncObserverToken: StoppableSpawn?
|
||||||
@@ -86,7 +86,7 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
self.backgroundTaskService = backgroundTaskService
|
self.backgroundTaskService = backgroundTaskService
|
||||||
clientQueue = .init(label: "ClientProxyQueue", attributes: .concurrent)
|
clientQueue = .init(label: "ClientProxyQueue", attributes: .concurrent)
|
||||||
|
|
||||||
mediaProxy = MediaProxy(client: client, clientQueue: clientQueue)
|
mediaLoader = MediaLoader(client: client, clientQueue: clientQueue)
|
||||||
|
|
||||||
client.setDelegate(delegate: WeakClientProxyWrapper(clientProxy: self))
|
client.setDelegate(delegate: WeakClientProxyWrapper(clientProxy: self))
|
||||||
|
|
||||||
@@ -401,16 +401,16 @@ class ClientProxy: ClientProxyProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ClientProxy: MediaProxyProtocol {
|
extension ClientProxy: MediaLoaderProtocol {
|
||||||
func mediaSourceForURL(_ url: URL) -> MediaSourceProxy {
|
func mediaSourceForURL(_ url: URL) async -> MediaSourceProxy {
|
||||||
mediaProxy.mediaSourceForURL(url)
|
await mediaLoader.mediaSourceForURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMediaContentForSource(_ source: MediaSourceProxy) async throws -> Data {
|
func loadMediaContentForSource(_ source: MediaSourceProxy) async throws -> Data {
|
||||||
try await mediaProxy.loadMediaContentForSource(source)
|
try await mediaLoader.loadMediaContentForSource(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadMediaThumbnailForSource(_ source: MediaSourceProxy, width: UInt, height: UInt) async throws -> Data {
|
func loadMediaThumbnailForSource(_ source: MediaSourceProxy, width: UInt, height: UInt) async throws -> Data {
|
||||||
try await mediaProxy.loadMediaThumbnailForSource(source, width: width, height: height)
|
try await mediaLoader.loadMediaThumbnailForSource(source, width: width, height: height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ enum PushFormat {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol ClientProxyProtocol: AnyObject, MediaProxyProtocol {
|
protocol ClientProxyProtocol: AnyObject, MediaLoaderProtocol {
|
||||||
var callbacks: PassthroughSubject<ClientProxyCallback, Never> { get }
|
var callbacks: PassthroughSubject<ClientProxyCallback, Never> { get }
|
||||||
|
|
||||||
var userID: String { get }
|
var userID: String { get }
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
protocol ImageProviderProtocol {
|
||||||
|
func imageFromSource(_ source: MediaSourceProxy?, size: CGSize?) -> UIImage?
|
||||||
|
|
||||||
|
@discardableResult func loadImageFromSource(_ source: MediaSourceProxy, size: CGSize?) async -> Result<UIImage, MediaProviderError>
|
||||||
|
|
||||||
|
func imageFromURL(_ url: URL?, size: CGSize?) -> UIImage?
|
||||||
|
|
||||||
|
@discardableResult func loadImageFromURL(_ url: URL, size: CGSize?) async -> Result<UIImage, MediaProviderError>
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ImageProviderProtocol {
|
||||||
|
func imageFromSource(_ source: MediaSourceProxy?) -> UIImage? {
|
||||||
|
imageFromSource(source, size: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult func loadImageFromSource(_ source: MediaSourceProxy) async -> Result<UIImage, MediaProviderError> {
|
||||||
|
await loadImageFromSource(source, size: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageFromURL(_ url: URL?) -> UIImage? {
|
||||||
|
imageFromURL(url, size: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@discardableResult func loadImageFromURL(_ url: URL) async -> Result<UIImage, MediaProviderError> {
|
||||||
|
await loadImageFromURL(url, size: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2022 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import MatrixRustSDK
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
private final class MediaRequest {
|
||||||
|
var continuations: [CheckedContinuation<Data, Error>] = []
|
||||||
|
}
|
||||||
|
|
||||||
|
actor MediaLoader: MediaLoaderProtocol {
|
||||||
|
private let client: ClientProtocol
|
||||||
|
private let clientQueue: DispatchQueue
|
||||||
|
private var ongoingRequests = [MediaSourceProxy: MediaRequest]()
|
||||||
|
|
||||||
|
init(client: ClientProtocol,
|
||||||
|
clientQueue: DispatchQueue = .global()) {
|
||||||
|
self.client = client
|
||||||
|
self.clientQueue = clientQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
func mediaSourceForURL(_ url: URL) -> MediaSourceProxy {
|
||||||
|
.init(url: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadMediaContentForSource(_ source: MediaSourceProxy) async throws -> Data {
|
||||||
|
try await enqueueLoadMediaRequest(forSource: source) {
|
||||||
|
try self.client.getMediaContent(source: source.underlyingSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadMediaThumbnailForSource(_ source: MediaSourceProxy, width: UInt, height: UInt) async throws -> Data {
|
||||||
|
try await enqueueLoadMediaRequest(forSource: source) {
|
||||||
|
try self.client.getMediaThumbnail(source: source.underlyingSource, width: UInt64(width), height: UInt64(height))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func enqueueLoadMediaRequest(forSource source: MediaSourceProxy, operation: @escaping () throws -> [UInt8]) async throws -> Data {
|
||||||
|
if let ongoingRequest = ongoingRequests[source] {
|
||||||
|
return try await withCheckedThrowingContinuation { continuation in
|
||||||
|
ongoingRequest.continuations.append(continuation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ongoingRequest = MediaRequest()
|
||||||
|
ongoingRequests[source] = ongoingRequest
|
||||||
|
|
||||||
|
defer {
|
||||||
|
ongoingRequests[source] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let result = try await Task.dispatch(on: clientQueue) {
|
||||||
|
let bytes = try operation()
|
||||||
|
return Data(bytes: bytes, count: bytes.count)
|
||||||
|
}
|
||||||
|
|
||||||
|
ongoingRequest.continuations.forEach { $0.resume(returning: result) }
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
ongoingRequest.continuations.forEach { $0.resume(throwing: error) }
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
protocol MediaProxyProtocol {
|
protocol MediaLoaderProtocol {
|
||||||
func mediaSourceForURL(_ url: URL) -> MediaSourceProxy
|
func mediaSourceForURL(_ url: URL) async -> MediaSourceProxy
|
||||||
|
|
||||||
func loadMediaContentForSource(_ source: MediaSourceProxy) async throws -> Data
|
func loadMediaContentForSource(_ source: MediaSourceProxy) async throws -> Data
|
||||||
|
|
||||||
@@ -18,53 +18,54 @@ import Kingfisher
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
struct MediaProvider: MediaProviderProtocol {
|
struct MediaProvider: MediaProviderProtocol {
|
||||||
private let mediaProxy: MediaProxyProtocol
|
private let mediaLoader: MediaLoaderProtocol
|
||||||
private let imageCache: Kingfisher.ImageCache
|
private let imageCache: Kingfisher.ImageCache
|
||||||
private let fileCache: FileCacheProtocol
|
private let fileCache: FileCacheProtocol
|
||||||
private let backgroundTaskService: BackgroundTaskServiceProtocol?
|
private let backgroundTaskService: BackgroundTaskServiceProtocol?
|
||||||
|
|
||||||
init(mediaProxy: MediaProxyProtocol,
|
init(mediaLoader: MediaLoaderProtocol,
|
||||||
imageCache: Kingfisher.ImageCache,
|
imageCache: Kingfisher.ImageCache,
|
||||||
fileCache: FileCacheProtocol,
|
fileCache: FileCacheProtocol,
|
||||||
backgroundTaskService: BackgroundTaskServiceProtocol?) {
|
backgroundTaskService: BackgroundTaskServiceProtocol?) {
|
||||||
self.mediaProxy = mediaProxy
|
self.mediaLoader = mediaLoader
|
||||||
self.imageCache = imageCache
|
self.imageCache = imageCache
|
||||||
self.fileCache = fileCache
|
self.fileCache = fileCache
|
||||||
self.backgroundTaskService = backgroundTaskService
|
self.backgroundTaskService = backgroundTaskService
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageFromSource(_ source: MediaSourceProxy?, avatarSize: AvatarSize?) -> UIImage? {
|
// MARK: Images
|
||||||
|
|
||||||
|
func imageFromSource(_ source: MediaSourceProxy?, size: CGSize?) -> UIImage? {
|
||||||
guard let url = source?.url else {
|
guard let url = source?.url else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
let cacheKey = cacheKeyForURL(url, avatarSize: avatarSize)
|
let cacheKey = cacheKeyForURL(url, size: size)
|
||||||
return imageCache.retrieveImageInMemoryCache(forKey: cacheKey, options: nil)
|
return imageCache.retrieveImageInMemoryCache(forKey: cacheKey, options: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageFromURL(_ url: URL?, avatarSize: AvatarSize?) -> UIImage? {
|
func imageFromURL(_ url: URL?, size: CGSize?) -> UIImage? {
|
||||||
guard let url else {
|
guard let url else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageFromSource(.init(url: url), avatarSize: avatarSize)
|
return imageFromSource(.init(url: url), size: size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadImageFromURL(_ url: URL, avatarSize: AvatarSize?) async -> Result<UIImage, MediaProviderError> {
|
func loadImageFromURL(_ url: URL, size: CGSize?) async -> Result<UIImage, MediaProviderError> {
|
||||||
await loadImageFromSource(.init(url: url), avatarSize: avatarSize)
|
await loadImageFromSource(.init(url: url), size: size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadImageFromSource(_ source: MediaSourceProxy, avatarSize: AvatarSize?) async -> Result<UIImage, MediaProviderError> {
|
func loadImageFromSource(_ source: MediaSourceProxy, size: CGSize?) async -> Result<UIImage, MediaProviderError> {
|
||||||
if let image = imageFromSource(source, avatarSize: avatarSize) {
|
if let image = imageFromSource(source, size: size) {
|
||||||
return .success(image)
|
return .success(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
#warning("Media loading should check for existing in flight operations and de-dupe requests.")
|
|
||||||
let loadImageBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadImage: \(source.url.hashValue)")
|
let loadImageBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadImage: \(source.url.hashValue)")
|
||||||
defer {
|
defer {
|
||||||
loadImageBgTask?.stop()
|
loadImageBgTask?.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
let cacheKey = cacheKeyForURL(source.url, avatarSize: avatarSize)
|
let cacheKey = cacheKeyForURL(source.url, size: size)
|
||||||
|
|
||||||
if case let .success(cacheResult) = await imageCache.retrieveImage(forKey: cacheKey),
|
if case let .success(cacheResult) = await imageCache.retrieveImage(forKey: cacheKey),
|
||||||
let image = cacheResult.image {
|
let image = cacheResult.image {
|
||||||
@@ -73,10 +74,10 @@ struct MediaProvider: MediaProviderProtocol {
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
let imageData: Data
|
let imageData: Data
|
||||||
if let avatarSize {
|
if let size {
|
||||||
imageData = try await mediaProxy.loadMediaThumbnailForSource(source, width: UInt(avatarSize.scaledValue), height: UInt(avatarSize.scaledValue))
|
imageData = try await mediaLoader.loadMediaThumbnailForSource(source, width: UInt(size.width), height: UInt(size.height))
|
||||||
} else {
|
} else {
|
||||||
imageData = try await mediaProxy.loadMediaContentForSource(source)
|
imageData = try await mediaLoader.loadMediaContentForSource(source)
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let image = UIImage(data: imageData) else {
|
guard let image = UIImage(data: imageData) else {
|
||||||
@@ -93,6 +94,8 @@ struct MediaProvider: MediaProviderProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Files
|
||||||
|
|
||||||
func fileFromSource(_ source: MediaSourceProxy?, fileExtension: String) -> URL? {
|
func fileFromSource(_ source: MediaSourceProxy?, fileExtension: String) -> URL? {
|
||||||
guard let source else {
|
guard let source else {
|
||||||
return nil
|
return nil
|
||||||
@@ -101,6 +104,18 @@ struct MediaProvider: MediaProviderProtocol {
|
|||||||
return fileCache.file(forKey: cacheKey, fileExtension: fileExtension)
|
return fileCache.file(forKey: cacheKey, fileExtension: fileExtension)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fileFromURL(_ url: URL?, fileExtension: String) -> URL? {
|
||||||
|
guard let url else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileFromSource(MediaSourceProxy(url: url), fileExtension: fileExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadFileFromURL(_ url: URL, fileExtension: String) async -> Result<URL, MediaProviderError> {
|
||||||
|
await loadFileFromSource(MediaSourceProxy(url: url), fileExtension: fileExtension)
|
||||||
|
}
|
||||||
|
|
||||||
@discardableResult func loadFileFromSource(_ source: MediaSourceProxy, fileExtension: String) async -> Result<URL, MediaProviderError> {
|
@discardableResult func loadFileFromSource(_ source: MediaSourceProxy, fileExtension: String) async -> Result<URL, MediaProviderError> {
|
||||||
if let url = fileFromSource(source, fileExtension: fileExtension) {
|
if let url = fileFromSource(source, fileExtension: fileExtension) {
|
||||||
return .success(url)
|
return .success(url)
|
||||||
@@ -114,7 +129,7 @@ struct MediaProvider: MediaProviderProtocol {
|
|||||||
let cacheKey = fileCacheKeyForURL(source.url)
|
let cacheKey = fileCacheKeyForURL(source.url)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let data = try await mediaProxy.loadMediaContentForSource(source)
|
let data = try await mediaLoader.loadMediaContentForSource(source)
|
||||||
|
|
||||||
let url = try fileCache.store(data, with: fileExtension, forKey: cacheKey)
|
let url = try fileCache.store(data, with: fileExtension, forKey: cacheKey)
|
||||||
return .success(url)
|
return .success(url)
|
||||||
@@ -124,23 +139,11 @@ struct MediaProvider: MediaProviderProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileFromURL(_ url: URL?, fileExtension: String) -> URL? {
|
|
||||||
guard let url else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileFromSource(MediaSourceProxy(url: url), fileExtension: fileExtension)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadFileFromURL(_ url: URL, fileExtension: String) async -> Result<URL, MediaProviderError> {
|
|
||||||
await loadFileFromSource(MediaSourceProxy(url: url), fileExtension: fileExtension)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private func cacheKeyForURL(_ url: URL, avatarSize: AvatarSize?) -> String {
|
private func cacheKeyForURL(_ url: URL, size: CGSize?) -> String {
|
||||||
if let avatarSize {
|
if let size {
|
||||||
return "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
return "\(url.absoluteString){\(size.width),\(size.height)}"
|
||||||
} else {
|
} else {
|
||||||
return url.absoluteString
|
return url.absoluteString
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,7 @@ enum MediaProviderError: Error {
|
|||||||
case invalidImageData
|
case invalidImageData
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol MediaProviderProtocol {
|
protocol MediaProviderProtocol: ImageProviderProtocol {
|
||||||
func imageFromSource(_ source: MediaSourceProxy?, avatarSize: AvatarSize?) -> UIImage?
|
|
||||||
|
|
||||||
@discardableResult func loadImageFromSource(_ source: MediaSourceProxy, avatarSize: AvatarSize?) async -> Result<UIImage, MediaProviderError>
|
|
||||||
|
|
||||||
func imageFromURL(_ url: URL?, avatarSize: AvatarSize?) -> UIImage?
|
|
||||||
|
|
||||||
@discardableResult func loadImageFromURL(_ url: URL, avatarSize: AvatarSize?) async -> Result<UIImage, MediaProviderError>
|
|
||||||
|
|
||||||
func fileFromSource(_ source: MediaSourceProxy?, fileExtension: String) -> URL?
|
func fileFromSource(_ source: MediaSourceProxy?, fileExtension: String) -> URL?
|
||||||
|
|
||||||
@discardableResult func loadFileFromSource(_ source: MediaSourceProxy, fileExtension: String) async -> Result<URL, MediaProviderError>
|
@discardableResult func loadFileFromSource(_ source: MediaSourceProxy, fileExtension: String) async -> Result<URL, MediaProviderError>
|
||||||
@@ -40,21 +32,3 @@ protocol MediaProviderProtocol {
|
|||||||
|
|
||||||
@discardableResult func loadFileFromURL(_ url: URL, fileExtension: String) async -> Result<URL, MediaProviderError>
|
@discardableResult func loadFileFromURL(_ url: URL, fileExtension: String) async -> Result<URL, MediaProviderError>
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MediaProviderProtocol {
|
|
||||||
func imageFromSource(_ source: MediaSourceProxy?) -> UIImage? {
|
|
||||||
imageFromSource(source, avatarSize: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult func loadImageFromSource(_ source: MediaSourceProxy) async -> Result<UIImage, MediaProviderError> {
|
|
||||||
await loadImageFromSource(source, avatarSize: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func imageFromURL(_ url: URL?) -> UIImage? {
|
|
||||||
imageFromURL(url, avatarSize: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult func loadImageFromURL(_ url: URL) async -> Result<UIImage, MediaProviderError> {
|
|
||||||
await loadImageFromURL(url, avatarSize: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright 2022 New Vector Ltd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Combine
|
|
||||||
import Foundation
|
|
||||||
import MatrixRustSDK
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
class MediaProxy: MediaProxyProtocol {
|
|
||||||
private let client: ClientProtocol
|
|
||||||
private let clientQueue: DispatchQueue
|
|
||||||
|
|
||||||
init(client: ClientProtocol,
|
|
||||||
clientQueue: DispatchQueue = .global()) {
|
|
||||||
self.client = client
|
|
||||||
self.clientQueue = clientQueue
|
|
||||||
}
|
|
||||||
|
|
||||||
func mediaSourceForURL(_ url: URL) -> MediaSourceProxy {
|
|
||||||
.init(url: url)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadMediaContentForSource(_ source: MediaSourceProxy) async throws -> Data {
|
|
||||||
try await Task.dispatch(on: clientQueue) {
|
|
||||||
let bytes = try self.client.getMediaContent(source: source.underlyingSource)
|
|
||||||
return Data(bytes: bytes, count: bytes.count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadMediaThumbnailForSource(_ source: MediaSourceProxy, width: UInt, height: UInt) async throws -> Data {
|
|
||||||
try await Task.dispatch(on: clientQueue) {
|
|
||||||
let bytes = try self.client.getMediaThumbnail(source: source.underlyingSource, width: UInt64(width), height: UInt64(height))
|
|
||||||
return Data(bytes: bytes, count: bytes.count)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -18,31 +18,31 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
struct MockMediaProvider: MediaProviderProtocol {
|
struct MockMediaProvider: MediaProviderProtocol {
|
||||||
func imageFromSource(_ source: MediaSourceProxy?, avatarSize: AvatarSize?) -> UIImage? {
|
func imageFromSource(_ source: MediaSourceProxy?, size: CGSize?) -> UIImage? {
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadImageFromSource(_ source: MediaSourceProxy, avatarSize: AvatarSize?) async -> Result<UIImage, MediaProviderError> {
|
func loadImageFromSource(_ source: MediaSourceProxy, size: CGSize?) async -> Result<UIImage, MediaProviderError> {
|
||||||
.failure(.failedRetrievingImage)
|
.failure(.failedRetrievingImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageFromURL(_ url: URL?, avatarSize: AvatarSize?) -> UIImage? {
|
func imageFromURL(_ url: URL?, size: CGSize?) -> UIImage? {
|
||||||
guard url != nil else {
|
guard url != nil else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if let avatarSize {
|
#warning("Fix me. this is stupid!")
|
||||||
switch avatarSize {
|
if let size {
|
||||||
case .room:
|
if size == AvatarSize.room(on: .details).scaledSize
|
||||||
|
|| size == AvatarSize.room(on: .home).scaledSize
|
||||||
|
|| size == AvatarSize.room(on: .timeline).scaledSize {
|
||||||
return Asset.Images.appLogo.image
|
return Asset.Images.appLogo.image
|
||||||
default:
|
|
||||||
return UIImage(systemName: "photo")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return UIImage(systemName: "photo")
|
return UIImage(systemName: "photo")
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadImageFromURL(_ url: URL, avatarSize: AvatarSize?) async -> Result<UIImage, MediaProviderError> {
|
func loadImageFromURL(_ url: URL, size: CGSize?) async -> Result<UIImage, MediaProviderError> {
|
||||||
guard let image = UIImage(systemName: "photo") else {
|
guard let image = UIImage(systemName: "photo") else {
|
||||||
fatalError()
|
fatalError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,20 +103,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let item = timelineItem as? EventBasedTimelineItemProtocol {
|
if let item = timelineItem as? EventBasedTimelineItemProtocol {
|
||||||
await loadUserAvatarForTimelineItem(item)
|
|
||||||
await loadUserDisplayNameForTimelineItem(item)
|
await loadUserDisplayNameForTimelineItem(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch timelineItem {
|
|
||||||
case let item as ImageRoomTimelineItem:
|
|
||||||
await loadThumbnailForImageTimelineItem(item)
|
|
||||||
case let item as VideoRoomTimelineItem:
|
|
||||||
await loadThumbnailForVideoTimelineItem(item)
|
|
||||||
case let item as StickerRoomTimelineItem:
|
|
||||||
await loadImageForStickerTimelineItem(item)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processItemDisappearance(_ itemID: String) { }
|
func processItemDisappearance(_ itemID: String) { }
|
||||||
@@ -339,78 +327,6 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
return .middle
|
return .middle
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadThumbnailForImageTimelineItem(_ timelineItem: ImageRoomTimelineItem) async {
|
|
||||||
if timelineItem.image != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let source = timelineItem.source else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch await mediaProvider.loadImageFromSource(source) {
|
|
||||||
case .success(let image):
|
|
||||||
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
|
|
||||||
var item = timelineItems[index] as? ImageRoomTimelineItem else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
item.image = image
|
|
||||||
timelineItems[index] = item
|
|
||||||
callbacks.send(.updatedTimelineItem(timelineItem.id))
|
|
||||||
case .failure:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadThumbnailForVideoTimelineItem(_ timelineItem: VideoRoomTimelineItem) async {
|
|
||||||
if timelineItem.image != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let source = timelineItem.thumbnailSource else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch await mediaProvider.loadImageFromSource(source) {
|
|
||||||
case .success(let image):
|
|
||||||
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
|
|
||||||
var item = timelineItems[index] as? VideoRoomTimelineItem else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
item.image = image
|
|
||||||
timelineItems[index] = item
|
|
||||||
callbacks.send(.updatedTimelineItem(timelineItem.id))
|
|
||||||
case .failure:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadImageForStickerTimelineItem(_ timelineItem: StickerRoomTimelineItem) async {
|
|
||||||
if timelineItem.image != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let url = timelineItem.imageURL else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch await mediaProvider.loadImageFromURL(url) {
|
|
||||||
case .success(let image):
|
|
||||||
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
|
|
||||||
var item = timelineItems[index] as? StickerRoomTimelineItem else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
item.image = image
|
|
||||||
timelineItems[index] = item
|
|
||||||
callbacks.send(.updatedTimelineItem(timelineItem.id))
|
|
||||||
case .failure:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func loadVideoForTimelineItem(_ timelineItem: VideoRoomTimelineItem) async {
|
private func loadVideoForTimelineItem(_ timelineItem: VideoRoomTimelineItem) async {
|
||||||
if timelineItem.cachedVideoURL != nil {
|
if timelineItem.cachedVideoURL != nil {
|
||||||
// already cached
|
// already cached
|
||||||
@@ -512,28 +428,6 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadUserAvatarForTimelineItem(_ timelineItem: EventBasedTimelineItemProtocol) async {
|
|
||||||
guard timelineItem.shouldShowSenderDetails,
|
|
||||||
let avatarURL = timelineItem.sender.avatarURL,
|
|
||||||
timelineItem.sender.avatar == nil else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch await mediaProvider.loadImageFromURL(avatarURL, avatarSize: .user(on: .timeline)) {
|
|
||||||
case .success(let avatar):
|
|
||||||
guard let index = timelineItems.firstIndex(where: { $0.id == timelineItem.id }),
|
|
||||||
var item = timelineItems[index] as? EventBasedTimelineItemProtocol else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
item.sender.avatar = avatar
|
|
||||||
timelineItems[index] = item
|
|
||||||
callbacks.send(.updatedTimelineItem(timelineItem.id))
|
|
||||||
case .failure:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#warning("This is here because sender profiles aren't working properly. Remove it entirely later")
|
#warning("This is here because sender profiles aren't working properly. Remove it entirely later")
|
||||||
private func loadUserDisplayNameForTimelineItem(_ timelineItem: EventBasedTimelineItemProtocol) async {
|
private func loadUserDisplayNameForTimelineItem(_ timelineItem: EventBasedTimelineItemProtocol) async {
|
||||||
if timelineItem.shouldShowSenderDetails == false || timelineItem.sender.displayName != nil {
|
if timelineItem.shouldShowSenderDetails == false || timelineItem.sender.displayName != nil {
|
||||||
|
|||||||
@@ -23,5 +23,4 @@ struct TimelineItemSender: Identifiable, Hashable {
|
|||||||
// Lazy loaded properties, displayName and avatarURL will be come lets.
|
// Lazy loaded properties, displayName and avatarURL will be come lets.
|
||||||
var displayName: String?
|
var displayName: String?
|
||||||
var avatarURL: URL?
|
var avatarURL: URL?
|
||||||
var avatar: UIImage?
|
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -27,7 +27,6 @@ struct ImageRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hash
|
|||||||
var sender: TimelineItemSender
|
var sender: TimelineItemSender
|
||||||
|
|
||||||
let source: MediaSourceProxy?
|
let source: MediaSourceProxy?
|
||||||
var image: UIImage?
|
|
||||||
var cachedFileURL: URL?
|
var cachedFileURL: URL?
|
||||||
|
|
||||||
var width: CGFloat?
|
var width: CGFloat?
|
||||||
|
|||||||
-1
@@ -29,7 +29,6 @@ struct VideoRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Hash
|
|||||||
let duration: UInt64
|
let duration: UInt64
|
||||||
let source: MediaSourceProxy?
|
let source: MediaSourceProxy?
|
||||||
let thumbnailSource: MediaSourceProxy?
|
let thumbnailSource: MediaSourceProxy?
|
||||||
var image: UIImage?
|
|
||||||
var cachedVideoURL: URL?
|
var cachedVideoURL: URL?
|
||||||
|
|
||||||
var width: CGFloat?
|
var width: CGFloat?
|
||||||
|
|||||||
+1
-2
@@ -26,8 +26,7 @@ struct StickerRoomTimelineItem: EventBasedTimelineItemProtocol, Identifiable, Ha
|
|||||||
|
|
||||||
var sender: TimelineItemSender
|
var sender: TimelineItemSender
|
||||||
|
|
||||||
let imageURL: URL?
|
let imageURL: URL
|
||||||
var image: UIImage?
|
|
||||||
|
|
||||||
var width: CGFloat?
|
var width: CGFloat?
|
||||||
var height: CGFloat?
|
var height: CGFloat?
|
||||||
|
|||||||
@@ -38,64 +38,61 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
// swiftlint:disable:next cyclomatic_complexity
|
// swiftlint:disable:next cyclomatic_complexity
|
||||||
func buildTimelineItemFor(eventItemProxy: EventTimelineItemProxy,
|
func buildTimelineItemFor(eventItemProxy: EventTimelineItemProxy,
|
||||||
groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol? {
|
groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol? {
|
||||||
var sender = eventItemProxy.sender
|
|
||||||
if let senderAvatarURL = eventItemProxy.sender.avatarURL,
|
|
||||||
let image = mediaProvider.imageFromURL(senderAvatarURL, avatarSize: .user(on: .timeline)) {
|
|
||||||
sender.avatar = image
|
|
||||||
}
|
|
||||||
|
|
||||||
let isOutgoing = eventItemProxy.isOwn
|
let isOutgoing = eventItemProxy.isOwn
|
||||||
|
|
||||||
switch eventItemProxy.content.kind() {
|
switch eventItemProxy.content.kind() {
|
||||||
case .unableToDecrypt(let encryptedMessage):
|
case .unableToDecrypt(let encryptedMessage):
|
||||||
return buildEncryptedTimelineItem(eventItemProxy, encryptedMessage, sender, isOutgoing, groupState)
|
return buildEncryptedTimelineItem(eventItemProxy, encryptedMessage, isOutgoing, groupState)
|
||||||
case .redactedMessage:
|
case .redactedMessage:
|
||||||
return buildRedactedTimelineItem(eventItemProxy, sender, isOutgoing, groupState)
|
return buildRedactedTimelineItem(eventItemProxy, isOutgoing, groupState)
|
||||||
case .sticker(let body, let imageInfo, let url):
|
case .sticker(let body, let imageInfo, let urlString):
|
||||||
return buildStickerTimelineItem(eventItemProxy, body, imageInfo, url, sender, isOutgoing, groupState)
|
guard let url = URL(string: urlString) else {
|
||||||
|
MXLog.error("Invalid sticker url string: \(urlString)")
|
||||||
|
return buildUnsupportedTimelineItem(eventItemProxy, "m.sticker", "Invalid Sticker URL", isOutgoing, groupState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildStickerTimelineItem(eventItemProxy, body, imageInfo, url, isOutgoing, groupState)
|
||||||
case .failedToParseMessageLike(let eventType, let error):
|
case .failedToParseMessageLike(let eventType, let error):
|
||||||
return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, sender, isOutgoing, groupState)
|
return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing, groupState)
|
||||||
case .failedToParseState(let eventType, _, let error):
|
case .failedToParseState(let eventType, _, let error):
|
||||||
return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, sender, isOutgoing, groupState)
|
return buildUnsupportedTimelineItem(eventItemProxy, eventType, error, isOutgoing, groupState)
|
||||||
case .message:
|
case .message:
|
||||||
guard let messageContent = eventItemProxy.content.asMessage() else { fatalError("Invalid message timeline item: \(eventItemProxy)") }
|
guard let messageContent = eventItemProxy.content.asMessage() else { fatalError("Invalid message timeline item: \(eventItemProxy)") }
|
||||||
|
|
||||||
switch messageContent.msgtype() {
|
switch messageContent.msgtype() {
|
||||||
case .text(content: let content):
|
case .text(content: let content):
|
||||||
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
||||||
return buildTextTimelineItemFromMessage(eventItemProxy, message, sender, isOutgoing, groupState)
|
return buildTextTimelineItemFromMessage(eventItemProxy, message, isOutgoing, groupState)
|
||||||
case .image(content: let content):
|
case .image(content: let content):
|
||||||
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
||||||
return buildImageTimelineItemFromMessage(eventItemProxy, message, sender, isOutgoing, groupState)
|
return buildImageTimelineItemFromMessage(eventItemProxy, message, isOutgoing, groupState)
|
||||||
case .video(let content):
|
case .video(let content):
|
||||||
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
||||||
return buildVideoTimelineItemFromMessage(eventItemProxy, message, sender, isOutgoing, groupState)
|
return buildVideoTimelineItemFromMessage(eventItemProxy, message, isOutgoing, groupState)
|
||||||
case .file(let content):
|
case .file(let content):
|
||||||
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
||||||
return buildFileTimelineItemFromMessage(eventItemProxy, message, sender, isOutgoing, groupState)
|
return buildFileTimelineItemFromMessage(eventItemProxy, message, isOutgoing, groupState)
|
||||||
case .notice(content: let content):
|
case .notice(content: let content):
|
||||||
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
||||||
return buildNoticeTimelineItemFromMessage(eventItemProxy, message, sender, isOutgoing, groupState)
|
return buildNoticeTimelineItemFromMessage(eventItemProxy, message, isOutgoing, groupState)
|
||||||
case .emote(content: let content):
|
case .emote(content: let content):
|
||||||
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
let message = MessageTimelineItem(item: eventItemProxy.item, content: content)
|
||||||
return buildEmoteTimelineItemFromMessage(eventItemProxy, message, sender, isOutgoing, groupState)
|
return buildEmoteTimelineItemFromMessage(eventItemProxy, message, isOutgoing, groupState)
|
||||||
case .none:
|
case .none:
|
||||||
return buildFallbackTimelineItem(eventItemProxy, sender, isOutgoing, groupState)
|
return buildFallbackTimelineItem(eventItemProxy, isOutgoing, groupState)
|
||||||
}
|
}
|
||||||
case .state(let stateKey, let content):
|
case .state(let stateKey, let content):
|
||||||
return buildStateTimelineItemFor(eventItemProxy: eventItemProxy, state: content, stateKey: stateKey, sender: sender, isOutgoing: isOutgoing)
|
return buildStateTimelineItemFor(eventItemProxy: eventItemProxy, state: content, stateKey: stateKey, isOutgoing: isOutgoing)
|
||||||
case .roomMembership(userId: let userID, change: let change):
|
case .roomMembership(userId: let userID, change: let change):
|
||||||
return buildStateMembershipChangeTimelineItemFor(eventItemProxy: eventItemProxy, member: userID, membershipChange: change, sender: sender, isOutgoing: isOutgoing)
|
return buildStateMembershipChangeTimelineItemFor(eventItemProxy: eventItemProxy, member: userID, membershipChange: change, isOutgoing: isOutgoing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Message Events
|
// MARK: - Message Events
|
||||||
|
|
||||||
// swiftlint:disable:next function_parameter_count
|
|
||||||
private func buildUnsupportedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildUnsupportedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ eventType: String,
|
_ eventType: String,
|
||||||
_ error: String,
|
_ error: String,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
UnsupportedRoomTimelineItem(id: eventItemProxy.id,
|
UnsupportedRoomTimelineItem(id: eventItemProxy.id,
|
||||||
@@ -106,7 +103,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
properties: RoomTimelineItemProperties())
|
properties: RoomTimelineItemProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,8 +111,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
private func buildStickerTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildStickerTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ body: String,
|
_ body: String,
|
||||||
_ imageInfo: ImageInfo,
|
_ imageInfo: ImageInfo,
|
||||||
_ imageURLString: String,
|
_ imageURL: URL,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
var aspectRatio: CGFloat?
|
var aspectRatio: CGFloat?
|
||||||
@@ -125,20 +121,14 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
aspectRatio = width / height
|
aspectRatio = width / height
|
||||||
}
|
}
|
||||||
|
|
||||||
var image: UIImage?
|
|
||||||
if let url = URL(string: imageURLString) {
|
|
||||||
image = mediaProvider.imageFromURL(url)
|
|
||||||
}
|
|
||||||
|
|
||||||
return StickerRoomTimelineItem(id: eventItemProxy.id,
|
return StickerRoomTimelineItem(id: eventItemProxy.id,
|
||||||
text: body,
|
text: body,
|
||||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
imageURL: URL(string: imageURLString),
|
imageURL: imageURL,
|
||||||
image: image,
|
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
aspectRatio: aspectRatio,
|
aspectRatio: aspectRatio,
|
||||||
@@ -149,7 +139,6 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
private func buildEncryptedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildEncryptedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ encryptedMessage: EncryptedMessage,
|
_ encryptedMessage: EncryptedMessage,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
var encryptionType = EncryptedRoomTimelineItem.EncryptionType.unknown
|
var encryptionType = EncryptedRoomTimelineItem.EncryptionType.unknown
|
||||||
@@ -169,12 +158,11 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
properties: RoomTimelineItemProperties())
|
properties: RoomTimelineItemProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildRedactedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildRedactedTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
RedactedRoomTimelineItem(id: eventItemProxy.id,
|
RedactedRoomTimelineItem(id: eventItemProxy.id,
|
||||||
@@ -183,12 +171,11 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
properties: RoomTimelineItemProperties())
|
properties: RoomTimelineItemProperties())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildFallbackTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildFallbackTimelineItem(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
let attributedText = attributedStringBuilder.fromPlain(eventItemProxy.body)
|
let attributedText = attributedStringBuilder.fromPlain(eventItemProxy.body)
|
||||||
@@ -201,14 +188,13 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
properties: RoomTimelineItemProperties(isEdited: eventItemProxy.content.asMessage()?.isEdited() ?? false,
|
properties: RoomTimelineItemProperties(isEdited: eventItemProxy.content.asMessage()?.isEdited() ?? false,
|
||||||
reactions: aggregateReactions(eventItemProxy.reactions)))
|
reactions: aggregateReactions(eventItemProxy.reactions)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildTextTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildTextTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ message: MessageTimelineItem<TextMessageContent>,
|
_ message: MessageTimelineItem<TextMessageContent>,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||||
@@ -221,7 +207,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
||||||
reactions: aggregateReactions(eventItemProxy.reactions),
|
reactions: aggregateReactions(eventItemProxy.reactions),
|
||||||
deliveryStatus: eventItemProxy.deliveryStatus))
|
deliveryStatus: eventItemProxy.deliveryStatus))
|
||||||
@@ -229,7 +215,6 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
private func buildImageTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildImageTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ message: MessageTimelineItem<ImageMessageContent>,
|
_ message: MessageTimelineItem<ImageMessageContent>,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
var aspectRatio: CGFloat?
|
var aspectRatio: CGFloat?
|
||||||
@@ -244,9 +229,8 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
source: message.source,
|
source: message.source,
|
||||||
image: mediaProvider.imageFromSource(message.source),
|
|
||||||
width: message.width,
|
width: message.width,
|
||||||
height: message.height,
|
height: message.height,
|
||||||
aspectRatio: aspectRatio,
|
aspectRatio: aspectRatio,
|
||||||
@@ -258,7 +242,6 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
private func buildVideoTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildVideoTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ message: MessageTimelineItem<VideoMessageContent>,
|
_ message: MessageTimelineItem<VideoMessageContent>,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
var aspectRatio: CGFloat?
|
var aspectRatio: CGFloat?
|
||||||
@@ -273,11 +256,10 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
duration: message.duration,
|
duration: message.duration,
|
||||||
source: message.source,
|
source: message.source,
|
||||||
thumbnailSource: message.thumbnailSource,
|
thumbnailSource: message.thumbnailSource,
|
||||||
image: mediaProvider.imageFromSource(message.thumbnailSource),
|
|
||||||
width: message.width,
|
width: message.width,
|
||||||
height: message.height,
|
height: message.height,
|
||||||
aspectRatio: aspectRatio,
|
aspectRatio: aspectRatio,
|
||||||
@@ -289,7 +271,6 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
private func buildFileTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildFileTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ message: MessageTimelineItem<FileMessageContent>,
|
_ message: MessageTimelineItem<FileMessageContent>,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
FileRoomTimelineItem(id: message.id,
|
FileRoomTimelineItem(id: message.id,
|
||||||
@@ -298,7 +279,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
source: message.source,
|
source: message.source,
|
||||||
thumbnailSource: message.thumbnailSource,
|
thumbnailSource: message.thumbnailSource,
|
||||||
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
||||||
@@ -308,7 +289,6 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
private func buildNoticeTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildNoticeTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ message: MessageTimelineItem<NoticeMessageContent>,
|
_ message: MessageTimelineItem<NoticeMessageContent>,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
let attributedText = (message.htmlBody != nil ? attributedStringBuilder.fromHTML(message.htmlBody) : attributedStringBuilder.fromPlain(message.body))
|
||||||
@@ -321,7 +301,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
||||||
reactions: aggregateReactions(eventItemProxy.reactions),
|
reactions: aggregateReactions(eventItemProxy.reactions),
|
||||||
deliveryStatus: eventItemProxy.deliveryStatus))
|
deliveryStatus: eventItemProxy.deliveryStatus))
|
||||||
@@ -329,10 +309,9 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
|
|
||||||
private func buildEmoteTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
private func buildEmoteTimelineItemFromMessage(_ eventItemProxy: EventTimelineItemProxy,
|
||||||
_ message: MessageTimelineItem<EmoteMessageContent>,
|
_ message: MessageTimelineItem<EmoteMessageContent>,
|
||||||
_ sender: TimelineItemSender,
|
|
||||||
_ isOutgoing: Bool,
|
_ isOutgoing: Bool,
|
||||||
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
_ groupState: TimelineItemGroupState) -> RoomTimelineItemProtocol {
|
||||||
let name = sender.displayName ?? sender.id
|
let name = eventItemProxy.sender.displayName ?? eventItemProxy.sender.id
|
||||||
|
|
||||||
var attributedText: AttributedString?
|
var attributedText: AttributedString?
|
||||||
if let htmlBody = message.htmlBody {
|
if let htmlBody = message.htmlBody {
|
||||||
@@ -350,7 +329,7 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
groupState: groupState,
|
groupState: groupState,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: eventItemProxy.isEditable,
|
isEditable: eventItemProxy.isEditable,
|
||||||
sender: sender,
|
sender: eventItemProxy.sender,
|
||||||
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
properties: RoomTimelineItemProperties(isEdited: message.isEdited,
|
||||||
reactions: aggregateReactions(eventItemProxy.reactions),
|
reactions: aggregateReactions(eventItemProxy.reactions),
|
||||||
deliveryStatus: eventItemProxy.deliveryStatus))
|
deliveryStatus: eventItemProxy.deliveryStatus))
|
||||||
@@ -368,28 +347,26 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
|
|||||||
private func buildStateTimelineItemFor(eventItemProxy: EventTimelineItemProxy,
|
private func buildStateTimelineItemFor(eventItemProxy: EventTimelineItemProxy,
|
||||||
state: OtherState,
|
state: OtherState,
|
||||||
stateKey: String,
|
stateKey: String,
|
||||||
sender: TimelineItemSender,
|
|
||||||
isOutgoing: Bool) -> RoomTimelineItemProtocol? {
|
isOutgoing: Bool) -> RoomTimelineItemProtocol? {
|
||||||
guard let text = stateEventStringBuilder.buildString(for: state, stateKey: stateKey, sender: sender, isOutgoing: isOutgoing) else { return nil }
|
guard let text = stateEventStringBuilder.buildString(for: state, stateKey: stateKey, sender: eventItemProxy.sender, isOutgoing: isOutgoing) else { return nil }
|
||||||
return buildStateTimelineItem(eventItemProxy: eventItemProxy, text: text, sender: sender, isOutgoing: isOutgoing)
|
return buildStateTimelineItem(eventItemProxy: eventItemProxy, text: text, isOutgoing: isOutgoing)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildStateMembershipChangeTimelineItemFor(eventItemProxy: EventTimelineItemProxy,
|
private func buildStateMembershipChangeTimelineItemFor(eventItemProxy: EventTimelineItemProxy,
|
||||||
member: String,
|
member: String,
|
||||||
membershipChange: MembershipChange,
|
membershipChange: MembershipChange,
|
||||||
sender: TimelineItemSender,
|
|
||||||
isOutgoing: Bool) -> RoomTimelineItemProtocol? {
|
isOutgoing: Bool) -> RoomTimelineItemProtocol? {
|
||||||
guard let text = stateEventStringBuilder.buildString(for: membershipChange, member: member, sender: eventItemProxy.sender, isOutgoing: isOutgoing) else { return nil }
|
guard let text = stateEventStringBuilder.buildString(for: membershipChange, member: member, sender: eventItemProxy.sender, isOutgoing: isOutgoing) else { return nil }
|
||||||
return buildStateTimelineItem(eventItemProxy: eventItemProxy, text: text, sender: sender, isOutgoing: isOutgoing)
|
return buildStateTimelineItem(eventItemProxy: eventItemProxy, text: text, isOutgoing: isOutgoing)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildStateTimelineItem(eventItemProxy: EventTimelineItemProxy, text: String, sender: TimelineItemSender, isOutgoing: Bool) -> RoomTimelineItemProtocol {
|
private func buildStateTimelineItem(eventItemProxy: EventTimelineItemProxy, text: String, isOutgoing: Bool) -> RoomTimelineItemProtocol {
|
||||||
StateRoomTimelineItem(id: eventItemProxy.id,
|
StateRoomTimelineItem(id: eventItemProxy.id,
|
||||||
text: text,
|
text: text,
|
||||||
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
timestamp: eventItemProxy.timestamp.formatted(date: .omitted, time: .shortened),
|
||||||
groupState: .single,
|
groupState: .single,
|
||||||
isOutgoing: isOutgoing,
|
isOutgoing: isOutgoing,
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
sender: sender)
|
sender: eventItemProxy.sender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,12 +190,12 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
|
|
||||||
let userNotificationController = UserNotificationController(rootCoordinator: settingsNavigationStackCoordinator)
|
let userNotificationController = UserNotificationController(rootCoordinator: settingsNavigationStackCoordinator)
|
||||||
|
|
||||||
let parameters = SettingsCoordinatorParameters(navigationStackCoordinator: settingsNavigationStackCoordinator,
|
let parameters = SettingsScreenCoordinatorParameters(navigationStackCoordinator: settingsNavigationStackCoordinator,
|
||||||
userNotificationController: userNotificationController,
|
userNotificationController: userNotificationController,
|
||||||
userSession: userSession,
|
userSession: userSession,
|
||||||
bugReportService: bugReportService)
|
bugReportService: bugReportService)
|
||||||
let settingsCoordinator = SettingsCoordinator(parameters: parameters)
|
let settingsScreenCoordinator = SettingsScreenCoordinator(parameters: parameters)
|
||||||
settingsCoordinator.callback = { [weak self] action in
|
settingsScreenCoordinator.callback = { [weak self] action in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
switch action {
|
switch action {
|
||||||
case .dismiss:
|
case .dismiss:
|
||||||
@@ -206,7 +206,7 @@ class UserSessionFlowCoordinator: CoordinatorProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsNavigationStackCoordinator.setRootCoordinator(settingsCoordinator)
|
settingsNavigationStackCoordinator.setRootCoordinator(settingsScreenCoordinator)
|
||||||
|
|
||||||
navigationSplitCoordinator.setSheetCoordinator(userNotificationController) { [weak self] in
|
navigationSplitCoordinator.setSheetCoordinator(userNotificationController) { [weak self] in
|
||||||
self?.stateMachine.processEvent(.dismissedSettingsScreen)
|
self?.stateMachine.processEvent(.dismissedSettingsScreen)
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class UserSessionStore: UserSessionStoreProtocol {
|
|||||||
imageCache.memoryStorage.config.keepWhenEnteringBackground = true
|
imageCache.memoryStorage.config.keepWhenEnteringBackground = true
|
||||||
|
|
||||||
return UserSession(clientProxy: clientProxy,
|
return UserSession(clientProxy: clientProxy,
|
||||||
mediaProvider: MediaProvider(mediaProxy: clientProxy,
|
mediaProvider: MediaProvider(mediaLoader: clientProxy,
|
||||||
imageCache: imageCache,
|
imageCache: imageCache,
|
||||||
fileCache: FileCache.default,
|
fileCache: FileCache.default,
|
||||||
backgroundTaskService: backgroundTaskService))
|
backgroundTaskService: backgroundTaskService))
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class MockScreen: Identifiable {
|
|||||||
return navigationStackCoordinator
|
return navigationStackCoordinator
|
||||||
case .settings:
|
case .settings:
|
||||||
let navigationStackCoordinator = NavigationStackCoordinator()
|
let navigationStackCoordinator = NavigationStackCoordinator()
|
||||||
let coordinator = SettingsCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator,
|
let coordinator = SettingsScreenCoordinator(parameters: .init(navigationStackCoordinator: navigationStackCoordinator,
|
||||||
userNotificationController: MockUserNotificationController(),
|
userNotificationController: MockUserNotificationController(),
|
||||||
userSession: MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"),
|
userSession: MockUserSession(clientProxy: MockClientProxy(userID: "@mock:client.com"),
|
||||||
mediaProvider: MockMediaProvider()),
|
mediaProvider: MockMediaProvider()),
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ class NotificationServiceExtension: UNNotificationServiceExtension {
|
|||||||
|
|
||||||
MXLog.info("\(tag) creating media provider")
|
MXLog.info("\(tag) creating media provider")
|
||||||
|
|
||||||
return MediaProvider(mediaProxy: MediaProxy(client: client),
|
return MediaProvider(mediaLoader: MediaLoader(client: client),
|
||||||
imageCache: .onlyOnDisk,
|
imageCache: .onlyOnDisk,
|
||||||
fileCache: FileCache.default,
|
fileCache: FileCache.default,
|
||||||
backgroundTaskService: nil)
|
backgroundTaskService: nil)
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ targets:
|
|||||||
sources:
|
sources:
|
||||||
- path: ../Sources
|
- path: ../Sources
|
||||||
- path: ../SupportingFiles
|
- path: ../SupportingFiles
|
||||||
|
- path: ../../ElementX/Sources/Generated
|
||||||
- path: ../../ElementX/Sources/Services/Timeline/TimelineItemProxy.swift
|
- path: ../../ElementX/Sources/Services/Timeline/TimelineItemProxy.swift
|
||||||
- path: ../../ElementX/Sources/Services/Timeline/TimelineItemSender.swift
|
- path: ../../ElementX/Sources/Services/Timeline/TimelineItemSender.swift
|
||||||
- path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift
|
- path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift
|
||||||
@@ -75,11 +76,7 @@ targets:
|
|||||||
- path: ../../ElementX/Sources/Services/Notification/Proxy/NotificationServiceProxy.swift
|
- path: ../../ElementX/Sources/Services/Notification/Proxy/NotificationServiceProxy.swift
|
||||||
- path: ../../ElementX/Sources/Services/Notification/Proxy/NotificationItemProxy.swift
|
- path: ../../ElementX/Sources/Services/Notification/Proxy/NotificationItemProxy.swift
|
||||||
- path: ../../ElementX/Sources/Services/Notification/NotificationConstants.swift
|
- path: ../../ElementX/Sources/Services/Notification/NotificationConstants.swift
|
||||||
- path: ../../ElementX/Sources/Services/Media/MediaProxyProtocol.swift
|
- path: ../../ElementX/Sources/Services/Media
|
||||||
- path: ../../ElementX/Sources/Services/Media/MediaProxy.swift
|
|
||||||
- path: ../../ElementX/Sources/Services/Media/MediaProviderProtocol.swift
|
|
||||||
- path: ../../ElementX/Sources/Services/Media/MediaProvider.swift
|
|
||||||
- path: ../../ElementX/Sources/Services/Media/MediaSourceProxy.swift
|
|
||||||
- path: ../../ElementX/Sources/Services/Background/BackgroundTaskServiceProtocol.swift
|
- path: ../../ElementX/Sources/Services/Background/BackgroundTaskServiceProtocol.swift
|
||||||
- path: ../../ElementX/Sources/Services/Background/BackgroundTaskProtocol.swift
|
- path: ../../ElementX/Sources/Services/Background/BackgroundTaskProtocol.swift
|
||||||
- path: ../../ElementX/Sources/Services/Cache/FileCache.swift
|
- path: ../../ElementX/Sources/Services/Cache/FileCache.swift
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class RoomScreenUITests: XCTestCase {
|
|||||||
app.goToScreenWithIdentifier(.roomPlainNoAvatar)
|
app.goToScreenWithIdentifier(.roomPlainNoAvatar)
|
||||||
|
|
||||||
XCTAssert(app.staticTexts["roomNameLabel"].exists)
|
XCTAssert(app.staticTexts["roomNameLabel"].exists)
|
||||||
XCTAssert(app.staticTexts["roomAvatarPlaceholderImage"].exists)
|
XCTAssert(app.staticTexts["roomAvatarImage"].exists)
|
||||||
|
|
||||||
app.assertScreenshot(.roomPlainNoAvatar)
|
app.assertScreenshot(.roomPlainNoAvatar)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2023 New Vector Ltd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
@testable import ElementX
|
||||||
|
import MatrixRustSDK
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class MediaLoaderTests: XCTestCase {
|
||||||
|
func testMediaRequestCoalescing() async {
|
||||||
|
let mediaLoadingClient = MockMediaLoadingClient()
|
||||||
|
let mediaLoader = MediaLoader(client: mediaLoadingClient)
|
||||||
|
|
||||||
|
let mediaSource = MediaSourceProxy(url: URL.documentsDirectory)
|
||||||
|
|
||||||
|
do {
|
||||||
|
for _ in 1...10 {
|
||||||
|
_ = try await mediaLoader.loadMediaContentForSource(mediaSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(mediaLoadingClient.numberOfInvocations, 10)
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMediaThumbnailRequestCoalescing() async {
|
||||||
|
let mediaLoadingClient = MockMediaLoadingClient()
|
||||||
|
let mediaLoader = MediaLoader(client: mediaLoadingClient)
|
||||||
|
|
||||||
|
let mediaSource = MediaSourceProxy(url: URL.documentsDirectory)
|
||||||
|
|
||||||
|
do {
|
||||||
|
for _ in 1...10 {
|
||||||
|
_ = try await mediaLoader.loadMediaThumbnailForSource(mediaSource, width: 100, height: 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertEqual(mediaLoadingClient.numberOfInvocations, 10)
|
||||||
|
} catch {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockMediaLoadingClient: ClientProtocol {
|
||||||
|
private(set) var numberOfInvocations = 0
|
||||||
|
|
||||||
|
func getMediaContent(source: MatrixRustSDK.MediaSource) throws -> [UInt8] {
|
||||||
|
numberOfInvocations += 1
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMediaThumbnail(source: MatrixRustSDK.MediaSource, width: UInt64, height: UInt64) throws -> [UInt8] {
|
||||||
|
numberOfInvocations += 1
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Not implemented
|
||||||
|
|
||||||
|
func setDelegate(delegate: MatrixRustSDK.ClientDelegate?) { }
|
||||||
|
|
||||||
|
func login(username: String, password: String, initialDeviceName: String?, deviceId: String?) throws { }
|
||||||
|
|
||||||
|
func restoreSession(session: MatrixRustSDK.Session) throws { }
|
||||||
|
|
||||||
|
func session() throws -> MatrixRustSDK.Session { fatalError() }
|
||||||
|
|
||||||
|
func userId() throws -> String { fatalError() }
|
||||||
|
|
||||||
|
func displayName() throws -> String { fatalError() }
|
||||||
|
|
||||||
|
func setDisplayName(name: String) throws { }
|
||||||
|
|
||||||
|
func avatarUrl() throws -> String { fatalError() }
|
||||||
|
|
||||||
|
func deviceId() throws -> String { fatalError() }
|
||||||
|
|
||||||
|
func accountData(eventType: String) throws -> String? { fatalError() }
|
||||||
|
|
||||||
|
func setAccountData(eventType: String, content: String) throws { fatalError() }
|
||||||
|
|
||||||
|
func uploadMedia(mimeType: String, content: [UInt8]) throws -> String { fatalError() }
|
||||||
|
|
||||||
|
func getSessionVerificationController() throws -> MatrixRustSDK.SessionVerificationController { fatalError() }
|
||||||
|
|
||||||
|
func fullSlidingSync() throws -> MatrixRustSDK.SlidingSync { fatalError() }
|
||||||
|
|
||||||
|
func logout() throws { }
|
||||||
|
|
||||||
|
func hasFirstSynced() -> Bool { fatalError() }
|
||||||
|
|
||||||
|
func homeserver() -> String { fatalError() }
|
||||||
|
|
||||||
|
func isSoftLogout() -> Bool { fatalError() }
|
||||||
|
|
||||||
|
func isSyncing() -> Bool { fatalError() }
|
||||||
|
|
||||||
|
func rooms() -> [MatrixRustSDK.Room] { fatalError() }
|
||||||
|
|
||||||
|
func slidingSync() -> MatrixRustSDK.SlidingSyncBuilder { fatalError() }
|
||||||
|
|
||||||
|
func startSync(timelineLimit: UInt16?) { }
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import XCTest
|
|||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class MediaProviderTests: XCTestCase {
|
final class MediaProviderTests: XCTestCase {
|
||||||
private let mediaProxy = MockMediaProxy()
|
private let mediaLoader = MockMediaLoader()
|
||||||
private let fileCache = MockFileCache()
|
private let fileCache = MockFileCache()
|
||||||
private var imageCache: MockImageCache!
|
private var imageCache: MockImageCache!
|
||||||
private var backgroundTaskService = MockBackgroundTaskService()
|
private var backgroundTaskService = MockBackgroundTaskService()
|
||||||
@@ -29,14 +29,14 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
imageCache = MockImageCache(name: "Test")
|
imageCache = MockImageCache(name: "Test")
|
||||||
mediaProvider = MediaProvider(mediaProxy: mediaProxy,
|
mediaProvider = MediaProvider(mediaLoader: mediaLoader,
|
||||||
imageCache: imageCache,
|
imageCache: imageCache,
|
||||||
fileCache: fileCache,
|
fileCache: fileCache,
|
||||||
backgroundTaskService: backgroundTaskService)
|
backgroundTaskService: backgroundTaskService)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenImageFromSourceWithSourceNil_nilReturned() throws {
|
func test_whenImageFromSourceWithSourceNil_nilReturned() throws {
|
||||||
let image = mediaProvider.imageFromSource(nil, avatarSize: .room(on: .timeline))
|
let image = mediaProvider.imageFromSource(nil, size: AvatarSize.room(on: .timeline).scaledSize)
|
||||||
XCTAssertNil(image)
|
XCTAssertNil(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,17 +46,18 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||||
let imageForKey = UIImage()
|
let imageForKey = UIImage()
|
||||||
imageCache.retrievedImagesInMemory[key] = imageForKey
|
imageCache.retrievedImagesInMemory[key] = imageForKey
|
||||||
let image = mediaProvider.imageFromSource(MediaSourceProxy(url: url), avatarSize: avatarSize)
|
let image = mediaProvider.imageFromSource(MediaSourceProxy(url: url), size: avatarSize.scaledSize)
|
||||||
XCTAssertEqual(image, imageForKey)
|
XCTAssertEqual(image, imageForKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws {
|
func test_whenImageFromSourceWithSourceNotNilAndImageNotCached_nilReturned() throws {
|
||||||
let image = mediaProvider.imageFromSource(MediaSourceProxy(url: URL.picturesDirectory), avatarSize: .room(on: .timeline))
|
let image = mediaProvider.imageFromSource(MediaSourceProxy(url: URL.picturesDirectory),
|
||||||
|
size: AvatarSize.room(on: .timeline).scaledSize)
|
||||||
XCTAssertNil(image)
|
XCTAssertNil(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenImageFromURLStringWithURLStringNil_nilReturned() throws {
|
func test_whenImageFromURLStringWithURLStringNil_nilReturned() throws {
|
||||||
let image = mediaProvider.imageFromURL(nil, avatarSize: .room(on: .timeline))
|
let image = mediaProvider.imageFromURL(nil, size: AvatarSize.room(on: .timeline).scaledSize)
|
||||||
XCTAssertNil(image)
|
XCTAssertNil(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,12 +67,12 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||||
let imageForKey = UIImage()
|
let imageForKey = UIImage()
|
||||||
imageCache.retrievedImagesInMemory[key] = imageForKey
|
imageCache.retrievedImagesInMemory[key] = imageForKey
|
||||||
let image = mediaProvider.imageFromURL(url, avatarSize: avatarSize)
|
let image = mediaProvider.imageFromURL(url, size: avatarSize.scaledSize)
|
||||||
XCTAssertEqual(image, imageForKey)
|
XCTAssertEqual(image, imageForKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenImageFromURLStringWithURLStringNotNilAndImageNotCached_nilReturned() throws {
|
func test_whenImageFromURLStringWithURLStringNotNilAndImageNotCached_nilReturned() throws {
|
||||||
let image = mediaProvider.imageFromURL(URL.picturesDirectory, avatarSize: .room(on: .timeline))
|
let image = mediaProvider.imageFromURL(URL.picturesDirectory, size: AvatarSize.room(on: .timeline).scaledSize)
|
||||||
XCTAssertNil(image)
|
XCTAssertNil(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||||
let imageForKey = UIImage()
|
let imageForKey = UIImage()
|
||||||
imageCache.retrievedImagesInMemory[key] = imageForKey
|
imageCache.retrievedImagesInMemory[key] = imageForKey
|
||||||
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url), avatarSize: avatarSize)
|
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url), size: avatarSize.scaledSize)
|
||||||
XCTAssertEqual(Result.success(imageForKey), result)
|
XCTAssertEqual(Result.success(imageForKey), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,15 +92,15 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||||
let imageForKey = UIImage()
|
let imageForKey = UIImage()
|
||||||
imageCache.retrievedImages[key] = imageForKey
|
imageCache.retrievedImages[key] = imageForKey
|
||||||
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url), avatarSize: avatarSize)
|
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url), size: avatarSize.scaledSize)
|
||||||
XCTAssertEqual(Result.success(imageForKey), result)
|
XCTAssertEqual(Result.success(imageForKey), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageThumbnailIsLoaded() async throws {
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFails_imageThumbnailIsLoaded() async throws {
|
||||||
let avatarSize = AvatarSize.room(on: .timeline)
|
let avatarSize = AvatarSize.room(on: .timeline)
|
||||||
let expectedImage = try loadTestImage()
|
let expectedImage = try loadTestImage()
|
||||||
mediaProxy.mediaThumbnailData = expectedImage.pngData()
|
mediaLoader.mediaThumbnailData = expectedImage.pngData()
|
||||||
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), avatarSize: avatarSize)
|
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), size: avatarSize.scaledSize)
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let image):
|
case .success(let image):
|
||||||
XCTAssertEqual(image.pngData(), expectedImage.pngData())
|
XCTAssertEqual(image.pngData(), expectedImage.pngData())
|
||||||
@@ -113,16 +114,16 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
let url = URL.picturesDirectory
|
let url = URL.picturesDirectory
|
||||||
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
let key = "\(url.absoluteString){\(avatarSize.scaledValue),\(avatarSize.scaledValue)}"
|
||||||
let expectedImage = try loadTestImage()
|
let expectedImage = try loadTestImage()
|
||||||
mediaProxy.mediaThumbnailData = expectedImage.pngData()
|
mediaLoader.mediaThumbnailData = expectedImage.pngData()
|
||||||
_ = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url), avatarSize: avatarSize)
|
_ = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: url), size: avatarSize.scaledSize)
|
||||||
let storedImage = try XCTUnwrap(imageCache.storedImages[key])
|
let storedImage = try XCTUnwrap(imageCache.storedImages[key])
|
||||||
XCTAssertEqual(expectedImage.pngData(), storedImage.pngData())
|
XCTAssertEqual(expectedImage.pngData(), storedImage.pngData())
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSize_imageContentIsLoaded() async throws {
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSize_imageContentIsLoaded() async throws {
|
||||||
let expectedImage = try loadTestImage()
|
let expectedImage = try loadTestImage()
|
||||||
mediaProxy.mediaContentData = expectedImage.pngData()
|
mediaLoader.mediaContentData = expectedImage.pngData()
|
||||||
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), avatarSize: nil)
|
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), size: nil)
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let image):
|
case .success(let image):
|
||||||
XCTAssertEqual(image.pngData(), expectedImage.pngData())
|
XCTAssertEqual(image.pngData(), expectedImage.pngData())
|
||||||
@@ -132,7 +133,8 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndLoadImageThumbnailFails_errorIsThrown() async throws {
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndLoadImageThumbnailFails_errorIsThrown() async throws {
|
||||||
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), avatarSize: AvatarSize.room(on: .timeline))
|
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory),
|
||||||
|
size: AvatarSize.room(on: .timeline).scaledSize)
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
XCTFail("Should fail")
|
XCTFail("Should fail")
|
||||||
@@ -142,7 +144,7 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSizeAndLoadImageContentFails_errorIsThrown() async throws {
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndNoAvatarSizeAndLoadImageContentFails_errorIsThrown() async throws {
|
||||||
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), avatarSize: nil)
|
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), size: nil)
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
XCTFail("Should fail")
|
XCTFail("Should fail")
|
||||||
@@ -152,8 +154,9 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndImageThumbnailIsLoadedWithCorruptedData_errorIsThrown() async throws {
|
func test_whenLoadImageFromSourceAndImageNotCachedAndRetrieveImageFailsAndImageThumbnailIsLoadedWithCorruptedData_errorIsThrown() async throws {
|
||||||
mediaProxy.mediaThumbnailData = Data()
|
mediaLoader.mediaThumbnailData = Data()
|
||||||
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory), avatarSize: AvatarSize.room(on: .timeline))
|
let result = await mediaProvider.loadImageFromSource(MediaSourceProxy(url: URL.picturesDirectory),
|
||||||
|
size: AvatarSize.room(on: .timeline).scaledSize)
|
||||||
switch result {
|
switch result {
|
||||||
case .success:
|
case .success:
|
||||||
XCTFail("Should fail")
|
XCTFail("Should fail")
|
||||||
@@ -187,31 +190,31 @@ final class MediaProviderTests: XCTestCase {
|
|||||||
func test_whenLoadFileFromSourceAndNoFileFromSourceExists_mediaLoadedFromSource() async throws {
|
func test_whenLoadFileFromSourceAndNoFileFromSourceExists_mediaLoadedFromSource() async throws {
|
||||||
let expectedURL = URL(filePath: "/some/file/path")
|
let expectedURL = URL(filePath: "/some/file/path")
|
||||||
let expectedResult: Result<URL, MediaProviderError> = .success(expectedURL)
|
let expectedResult: Result<URL, MediaProviderError> = .success(expectedURL)
|
||||||
mediaProxy.mediaContentData = try loadTestImage().pngData()
|
mediaLoader.mediaContentData = try loadTestImage().pngData()
|
||||||
fileCache.storeURLToReturn = expectedURL
|
fileCache.storeURLToReturn = expectedURL
|
||||||
let result = await mediaProvider.loadFileFromSource(MediaSourceProxy(url: URL(staticString: "test/test1")), fileExtension: "png")
|
let result = await mediaProvider.loadFileFromSource(MediaSourceProxy(url: URL(staticString: "test/test1")), fileExtension: "png")
|
||||||
XCTAssertEqual(result, expectedResult)
|
XCTAssertEqual(result, expectedResult)
|
||||||
XCTAssertEqual(mediaProxy.mediaContentData, fileCache.storedData)
|
XCTAssertEqual(mediaLoader.mediaContentData, fileCache.storedData)
|
||||||
XCTAssertEqual("test1", fileCache.storedFileKey)
|
XCTAssertEqual("test1", fileCache.storedFileKey)
|
||||||
XCTAssertEqual("png", fileCache.storedFileExtension)
|
XCTAssertEqual("png", fileCache.storedFileExtension)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenLoadFileFromSourceAndNoFileFromSourceExistsAndLoadContentSourceFails_failureIsReturned() async throws {
|
func test_whenLoadFileFromSourceAndNoFileFromSourceExistsAndLoadContentSourceFails_failureIsReturned() async throws {
|
||||||
let expectedResult: Result<URL, MediaProviderError> = .failure(.failedRetrievingImage)
|
let expectedResult: Result<URL, MediaProviderError> = .failure(.failedRetrievingImage)
|
||||||
mediaProxy.mediaContentData = nil
|
mediaLoader.mediaContentData = nil
|
||||||
let result = await mediaProvider.loadFileFromSource(MediaSourceProxy(url: URL(staticString: "test/test1")), fileExtension: "png")
|
let result = await mediaProvider.loadFileFromSource(MediaSourceProxy(url: URL(staticString: "test/test1")), fileExtension: "png")
|
||||||
XCTAssertEqual(result, expectedResult)
|
XCTAssertEqual(result, expectedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenLoadFileFromSourceAndNoFileFromSourceExistsAndStoreDataFails_failureIsReturned() async throws {
|
func test_whenLoadFileFromSourceAndNoFileFromSourceExistsAndStoreDataFails_failureIsReturned() async throws {
|
||||||
let expectedResult: Result<URL, MediaProviderError> = .failure(.failedRetrievingImage)
|
let expectedResult: Result<URL, MediaProviderError> = .failure(.failedRetrievingImage)
|
||||||
mediaProxy.mediaContentData = try loadTestImage().pngData()
|
mediaLoader.mediaContentData = try loadTestImage().pngData()
|
||||||
let result = await mediaProvider.loadFileFromSource(MediaSourceProxy(url: URL(staticString: "test/test1")), fileExtension: "png")
|
let result = await mediaProvider.loadFileFromSource(MediaSourceProxy(url: URL(staticString: "test/test1")), fileExtension: "png")
|
||||||
XCTAssertEqual(result, expectedResult)
|
XCTAssertEqual(result, expectedResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
func test_whenFileFromURLStringAndURLIsNil_nilIsReturned() async throws {
|
func test_whenFileFromURLStringAndURLIsNil_nilIsReturned() async throws {
|
||||||
mediaProxy.mediaContentData = try loadTestImage().pngData()
|
mediaLoader.mediaContentData = try loadTestImage().pngData()
|
||||||
let url = mediaProvider.fileFromURL(nil, fileExtension: "png")
|
let url = mediaProvider.fileFromURL(nil, fileExtension: "png")
|
||||||
XCTAssertNil(url)
|
XCTAssertNil(url)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -16,11 +16,11 @@
|
|||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum MockMediaProxyError: Error {
|
enum MockMediaLoaderError: Error {
|
||||||
case someError
|
case someError
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockMediaProxy: MediaProxyProtocol {
|
class MockMediaLoader: MediaLoaderProtocol {
|
||||||
var mediaContentData: Data?
|
var mediaContentData: Data?
|
||||||
var mediaThumbnailData: Data?
|
var mediaThumbnailData: Data?
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ class MockMediaProxy: MediaProxyProtocol {
|
|||||||
if let mediaContentData {
|
if let mediaContentData {
|
||||||
return mediaContentData
|
return mediaContentData
|
||||||
} else {
|
} else {
|
||||||
throw MockMediaProxyError.someError
|
throw MockMediaLoaderError.someError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ class MockMediaProxy: MediaProxyProtocol {
|
|||||||
if let mediaThumbnailData {
|
if let mediaThumbnailData {
|
||||||
return mediaThumbnailData
|
return mediaThumbnailData
|
||||||
} else {
|
} else {
|
||||||
throw MockMediaProxyError.someError
|
throw MockMediaLoaderError.someError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,14 +19,14 @@ import XCTest
|
|||||||
@testable import ElementX
|
@testable import ElementX
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class SettingsViewModelTests: XCTestCase {
|
class SettingsScreenViewModelTests: XCTestCase {
|
||||||
var viewModel: SettingsViewModelProtocol!
|
var viewModel: SettingsScreenViewModelProtocol!
|
||||||
var context: SettingsViewModelType.Context!
|
var context: SettingsScreenViewModelType.Context!
|
||||||
|
|
||||||
@MainActor override func setUpWithError() throws {
|
@MainActor override func setUpWithError() throws {
|
||||||
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: ""),
|
let userSession = MockUserSession(clientProxy: MockClientProxy(userID: ""),
|
||||||
mediaProvider: MockMediaProvider())
|
mediaProvider: MockMediaProvider())
|
||||||
viewModel = SettingsViewModel(withUserSession: userSession)
|
viewModel = SettingsScreenViewModel(withUserSession: userSession)
|
||||||
context = viewModel.context
|
context = viewModel.context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user