Pinned events banner loading state (#3118)
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 56;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
@@ -731,6 +731,8 @@
|
||||
A6F345328CCC5C9B0DAE2257 /* LogViewerScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB05221D7D941CC82DC8480 /* LogViewerScreenViewModel.swift */; };
|
||||
A722F426FD81FC67706BB1E0 /* CustomLayoutLabelStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42236480CF0431535EBE8387 /* CustomLayoutLabelStyle.swift */; };
|
||||
A74438ED16F8683A4B793E6A /* AnalyticsSettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BCE3FAF40932AC7C7639AC4 /* AnalyticsSettingsScreenViewModel.swift */; };
|
||||
A7580E042C622A5300A0991D /* PinnedEventsBannerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A7580E032C622A5300A0991D /* PinnedEventsBannerStateTests.swift */; };
|
||||
A75B3BF22C61460B000CE956 /* NetworkMonitorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */; };
|
||||
A7D48E44D485B143AADDB77D /* Strings+Untranslated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A18F6CE4D694D21E4EA9B25 /* Strings+Untranslated.swift */; };
|
||||
A816F7087C495D85048AC50E /* RoomMemberDetailsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B6E30BB748F3F480F077969 /* RoomMemberDetailsScreenModels.swift */; };
|
||||
A851635B3255C6DC07034A12 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8108C8F0ACF6A7EB72D0117 /* RoomScreenCoordinator.swift */; };
|
||||
@@ -1174,13 +1176,13 @@
|
||||
033DB41C51865A2E83174E87 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
|
||||
035177BCD8E8308B098AC3C2 /* WindowManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowManager.swift; sourceTree = "<group>"; };
|
||||
0376C429FAB1687C3D905F3E /* MockCoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCoder.swift; sourceTree = "<group>"; };
|
||||
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; path = test_voice_message.m4a; sourceTree = "<group>"; };
|
||||
0392E3FDE372C9B56FEEED8B /* test_voice_message.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = test_voice_message.m4a; sourceTree = "<group>"; };
|
||||
03DD998E523D4EC93C7ED703 /* RoomNotificationSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
03FABD73FD8086EFAB699F42 /* MediaUploadPreviewScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaUploadPreviewScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
044E501B8331B339874D1B96 /* CompoundIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompoundIcon.swift; sourceTree = "<group>"; };
|
||||
045253F9967A535EE5B16691 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
|
||||
046C0D3F53B0B5EF0A1F5BEA /* RoomSummaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomSummaryTests.swift; sourceTree = "<group>"; };
|
||||
048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
048A21188AB19349D026BECD /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessibilityIdentifiers.swift; sourceTree = "<group>"; };
|
||||
04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager.swift; sourceTree = "<group>"; };
|
||||
0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
@@ -1240,7 +1242,7 @@
|
||||
127C8472672A5BA09EF1ACF8 /* CurrentValuePublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentValuePublisher.swift; sourceTree = "<group>"; };
|
||||
128501375217576AF0FE3E92 /* RoomAttachmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAttachmentPicker.swift; sourceTree = "<group>"; };
|
||||
12F1E7F9C2BE8BB751037826 /* WaitlistScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; path = IntegrationTests.xctestplan; sourceTree = "<group>"; };
|
||||
1304D9191300873EADA52D6E /* IntegrationTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = IntegrationTests.xctestplan; sourceTree = "<group>"; };
|
||||
130ED565A078F7E0B59D9D25 /* UNTextInputNotificationResponse+Creator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNTextInputNotificationResponse+Creator.swift"; sourceTree = "<group>"; };
|
||||
136F80A613B55BDD071DCEA5 /* JoinRoomScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenModels.swift; sourceTree = "<group>"; };
|
||||
13802897C7AFA360EA74C0B0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = en; path = en.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
|
||||
@@ -1331,7 +1333,7 @@
|
||||
25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxy.swift; sourceTree = "<group>"; };
|
||||
25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
260004737C573A56FA01E86E /* Encodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encodable.swift; sourceTree = "<group>"; };
|
||||
267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; path = PreviewTests.xctestplan; sourceTree = "<group>"; };
|
||||
267BB1D5B08A9511F894CB57 /* PreviewTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = PreviewTests.xctestplan; sourceTree = "<group>"; };
|
||||
26B0A96B8FE4849227945067 /* VoiceMessageRecorder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceMessageRecorder.swift; sourceTree = "<group>"; };
|
||||
26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockServiceProtocol.swift; sourceTree = "<group>"; };
|
||||
2721D7B051F0159AA919DA05 /* RoomChangePermissionsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangePermissionsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
@@ -1396,7 +1398,7 @@
|
||||
3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = "<group>"; };
|
||||
35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
35FA991289149D31F4286747 /* UserPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreference.swift; sourceTree = "<group>"; };
|
||||
36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
36DA824791172B9821EACBED /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
||||
36FD673E24FBFCFDF398716A /* RoomMemberProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberProxyMock.swift; sourceTree = "<group>"; };
|
||||
376D941BF8BB294389C0DE24 /* MapTilerURLBuildersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerURLBuildersTests.swift; sourceTree = "<group>"; };
|
||||
37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItem.swift; sourceTree = "<group>"; };
|
||||
@@ -1745,7 +1747,7 @@
|
||||
8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilder.swift; 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>"; };
|
||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; path = UITests.xctestplan; sourceTree = "<group>"; };
|
||||
8E088F2A1B9EC529D3221931 /* UITests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UITests.xctestplan; sourceTree = "<group>"; };
|
||||
8E1584F8BCF407BB94F48F04 /* EncryptionResetPasswordScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreen.swift; sourceTree = "<group>"; };
|
||||
8F21ED7205048668BEB44A38 /* AppActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppActivityView.swift; sourceTree = "<group>"; };
|
||||
8F6210134203BE1F2DD5C679 /* RoomDirectoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectoryCell.swift; sourceTree = "<group>"; };
|
||||
@@ -1840,6 +1842,8 @@
|
||||
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PHGPostHogConfiguration.swift; sourceTree = "<group>"; };
|
||||
A6C11AD9813045E44F950410 /* ElementCallWidgetDriverProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementCallWidgetDriverProtocol.swift; sourceTree = "<group>"; };
|
||||
A73A07BAEDD74C48795A996A /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = "<group>"; };
|
||||
A7580E032C622A5300A0991D /* PinnedEventsBannerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedEventsBannerStateTests.swift; sourceTree = "<group>"; };
|
||||
A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitorMock.swift; sourceTree = "<group>"; };
|
||||
A7C4EA55DA62F9D0F984A2AE /* CollapsibleTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleTimelineItem.swift; sourceTree = "<group>"; };
|
||||
A7D452AF7B5F7E3A0A7DB54C /* SessionVerificationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
A7E37072597F67C4DD8CC2DB /* ComposerDraftServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerDraftServiceProtocol.swift; sourceTree = "<group>"; };
|
||||
@@ -1909,7 +1913,7 @@
|
||||
B53AC78E49A297AC1D72A7CF /* AppMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediator.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>"; };
|
||||
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
||||
B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = ConfettiScene.scn; sourceTree = "<group>"; };
|
||||
B6311F21F911E23BE4DF51B4 /* ReadMarkerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadMarkerRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
B63B69F9A2BC74DD40DC75C8 /* AdvancedSettingsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
B6404166CBF5CC88673FF9E2 /* RoomDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetails.swift; sourceTree = "<group>"; };
|
||||
@@ -2022,7 +2026,7 @@
|
||||
CE47A97726F0675DEE387BF9 /* TypingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypingIndicatorView.swift; sourceTree = "<group>"; };
|
||||
CEE0E6043EFCF6FD2A341861 /* TimelineReplyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReplyView.swift; sourceTree = "<group>"; };
|
||||
CEE20623EB4A9B88FB29F2BA /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||
CEE41494C837AA403A06A5D9 /* UnitTests.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = UnitTests.xctestplan; sourceTree = "<group>"; };
|
||||
D071F86CD47582B9196C9D16 /* UserDiscoverySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDiscoverySection.swift; sourceTree = "<group>"; };
|
||||
D086854995173E897F993C26 /* AdvancedSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
D09A267106B9585D3D0CFC0D /* ClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientError.swift; sourceTree = "<group>"; };
|
||||
@@ -2150,7 +2154,7 @@
|
||||
ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRoomCell.swift; sourceTree = "<group>"; };
|
||||
ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||
ED33988DA4FD4FC666800106 /* SessionVerificationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; path = message.caf; sourceTree = "<group>"; };
|
||||
ED482057AE39D5C6D9C5F3D8 /* message.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = message.caf; sourceTree = "<group>"; };
|
||||
ED60E4D2CD678E1EBF16F77A /* BlockedUsersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockedUsersScreen.swift; sourceTree = "<group>"; };
|
||||
ED983D4DCA5AFA6E1ED96099 /* StateRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StateRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
EDAA4472821985BF868CC21C /* ServerSelectionViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionViewModelTests.swift; sourceTree = "<group>"; };
|
||||
@@ -2173,7 +2177,7 @@
|
||||
F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenModels.swift; sourceTree = "<group>"; };
|
||||
F17EFA1D3D09FC2F9C5E1CB2 /* MediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProvider.swift; sourceTree = "<group>"; };
|
||||
F1B8500C152BC59445647DA8 /* UnsupportedRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsupportedRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; path = portrait_test_video.mp4; sourceTree = "<group>"; };
|
||||
F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = portrait_test_video.mp4; sourceTree = "<group>"; };
|
||||
F2E4EF80DFB8FE7C4469B15D /* RoomDirectorySearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreen.swift; sourceTree = "<group>"; };
|
||||
F31F59030205A6F65B057E1A /* MatrixEntityRegexTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegexTests.swift; sourceTree = "<group>"; };
|
||||
F348B5F2C12F9D4F4B4D3884 /* VideoRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||
@@ -2773,6 +2777,7 @@
|
||||
AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */,
|
||||
F4469F6AE311BDC439B3A5EC /* UserSessionMock.swift */,
|
||||
B23135B06B044CB811139D2F /* Generated */,
|
||||
A75B3BF12C61460B000CE956 /* NetworkMonitorMock.swift */,
|
||||
);
|
||||
path = Mocks;
|
||||
sourceTree = "<group>";
|
||||
@@ -3738,6 +3743,7 @@
|
||||
7583EAC171059A86B767209F /* MediaProvider */,
|
||||
7DBC911559934065993A5FF4 /* NotificationManager */,
|
||||
1C62F5382CC9D9F7DCEC344A /* UserDiscoveryService */,
|
||||
A7580E032C622A5300A0991D /* PinnedEventsBannerStateTests.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
@@ -5979,6 +5985,7 @@
|
||||
04F17DE71A50206336749BAC /* UserPreferenceTests.swift in Sources */,
|
||||
73F547BEB41D3DAFAAF6E0AF /* UserProfileScreenViewModelTests.swift in Sources */,
|
||||
627139A3D79F032BA81E3A53 /* UserSessionFlowCoordinatorTests.swift in Sources */,
|
||||
A7580E042C622A5300A0991D /* PinnedEventsBannerStateTests.swift in Sources */,
|
||||
81A7C020CB5F6232242A8414 /* UserSessionTests.swift in Sources */,
|
||||
21AFEFB8CEFE56A3811A1F5B /* VoiceMessageCacheTests.swift in Sources */,
|
||||
44BDD670FF9095ACE240A3A2 /* VoiceMessageMediaManagerTests.swift in Sources */,
|
||||
@@ -6005,6 +6012,7 @@
|
||||
34433A509DFEC93579B3B35B /* AdvancedSettingsScreen.swift in Sources */,
|
||||
4557192F5B15A8D9BB920232 /* AdvancedSettingsScreenCoordinator.swift in Sources */,
|
||||
0A194F5E70B5A628C1BF4476 /* AdvancedSettingsScreenModels.swift in Sources */,
|
||||
A75B3BF22C61460B000CE956 /* NetworkMonitorMock.swift in Sources */,
|
||||
B879446FD8E65A711EF8F9F7 /* AdvancedSettingsScreenViewModel.swift in Sources */,
|
||||
62910B515BCB4B455E24D7C1 /* AdvancedSettingsScreenViewModelProtocol.swift in Sources */,
|
||||
53C1E7F6A7D6409D89F36ED7 /* AggregatedReactionMock.swift in Sources */,
|
||||
@@ -7016,9 +7024,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||
OTHER_SWIFT_FLAGS = (
|
||||
"-DIS_NSE",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "-DIS_NSE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse";
|
||||
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
|
||||
PRODUCT_NAME = NSE;
|
||||
@@ -7067,9 +7073,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||
OTHER_SWIFT_FLAGS = (
|
||||
"-DIS_MAIN_APP",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP";
|
||||
PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)";
|
||||
PRODUCT_NAME = "$(APP_NAME)";
|
||||
@@ -7095,9 +7099,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||
OTHER_SWIFT_FLAGS = (
|
||||
"-DIS_MAIN_APP",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "-DIS_MAIN_APP";
|
||||
PILLS_UT_TYPE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER).pills";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "$(BASE_BUNDLE_IDENTIFIER)";
|
||||
PRODUCT_NAME = "$(APP_NAME)";
|
||||
@@ -7342,9 +7344,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(MARKETING_VERSION)";
|
||||
OTHER_SWIFT_FLAGS = (
|
||||
"-DIS_NSE",
|
||||
);
|
||||
OTHER_SWIFT_FLAGS = "-DIS_NSE";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "${BASE_BUNDLE_IDENTIFIER}.nse";
|
||||
PRODUCT_DISPLAY_NAME = "$(APP_DISPLAY_NAME)";
|
||||
PRODUCT_NAME = NSE;
|
||||
@@ -7561,7 +7561,7 @@
|
||||
repositoryURL = "https://github.com/element-hq/matrix-rust-components-swift";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 1.0.31;
|
||||
version = 1.0.32;
|
||||
};
|
||||
};
|
||||
701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */ = {
|
||||
|
||||
@@ -149,8 +149,8 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/element-hq/matrix-rust-components-swift",
|
||||
"state" : {
|
||||
"revision" : "8e2b4049fb492dcf5b0c796784b7aa7a3c099943",
|
||||
"version" : "1.0.31"
|
||||
"revision" : "9a9d116e5f00b31ad4f727d0875fed13a230806b",
|
||||
"version" : "1.0.32"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -318,6 +318,7 @@
|
||||
"screen_room_mentions_at_room_subtitle" = "Notify the whole room";
|
||||
"screen_room_pinned_banner_indicator" = "%1$@ of %2$@";
|
||||
"screen_room_pinned_banner_indicator_description" = "%1$@ Pinned messages";
|
||||
"screen_room_pinned_banner_loading_description" = "Loading message…";
|
||||
"screen_room_pinned_banner_view_all_button_title" = "View All";
|
||||
"screen_account_provider_change" = "Change account provider";
|
||||
"screen_account_provider_form_hint" = "Homeserver address";
|
||||
@@ -851,6 +852,12 @@
|
||||
"state_event_room_name_removed_by_you" = "You removed the room name";
|
||||
"state_event_room_none" = "%1$@ made no changes";
|
||||
"state_event_room_none_by_you" = "You made no changes";
|
||||
"state_event_room_pinned_events_changed" = "%1$@ changed the pinned messages";
|
||||
"state_event_room_pinned_events_changed_by_you" = "You changed the pinned messages";
|
||||
"state_event_room_pinned_events_pinned" = "%1$@ pinned a message";
|
||||
"state_event_room_pinned_events_pinned_by_you" = "You pinned a message";
|
||||
"state_event_room_pinned_events_unpinned" = "%1$@ unpinned a message";
|
||||
"state_event_room_pinned_events_unpinned_by_you" = "You unpinned a message";
|
||||
"state_event_room_reject" = "%1$@ rejected the invitation";
|
||||
"state_event_room_reject_by_you" = "You rejected the invitation";
|
||||
"state_event_room_remove" = "%1$@ removed %2$@";
|
||||
|
||||
@@ -1675,6 +1675,8 @@ internal enum L10n {
|
||||
internal static func screenRoomPinnedBannerIndicatorDescription(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_room_pinned_banner_indicator_description", String(describing: p1))
|
||||
}
|
||||
/// Loading message…
|
||||
internal static var screenRoomPinnedBannerLoadingDescription: String { return L10n.tr("Localizable", "screen_room_pinned_banner_loading_description") }
|
||||
/// View All
|
||||
internal static var screenRoomPinnedBannerViewAllButtonTitle: String { return L10n.tr("Localizable", "screen_room_pinned_banner_view_all_button_title") }
|
||||
/// Send again
|
||||
@@ -2111,6 +2113,24 @@ internal enum L10n {
|
||||
}
|
||||
/// You made no changes
|
||||
internal static var stateEventRoomNoneByYou: String { return L10n.tr("Localizable", "state_event_room_none_by_you") }
|
||||
/// %1$@ changed the pinned messages
|
||||
internal static func stateEventRoomPinnedEventsChanged(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "state_event_room_pinned_events_changed", String(describing: p1))
|
||||
}
|
||||
/// You changed the pinned messages
|
||||
internal static var stateEventRoomPinnedEventsChangedByYou: String { return L10n.tr("Localizable", "state_event_room_pinned_events_changed_by_you") }
|
||||
/// %1$@ pinned a message
|
||||
internal static func stateEventRoomPinnedEventsPinned(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "state_event_room_pinned_events_pinned", String(describing: p1))
|
||||
}
|
||||
/// You pinned a message
|
||||
internal static var stateEventRoomPinnedEventsPinnedByYou: String { return L10n.tr("Localizable", "state_event_room_pinned_events_pinned_by_you") }
|
||||
/// %1$@ unpinned a message
|
||||
internal static func stateEventRoomPinnedEventsUnpinned(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "state_event_room_pinned_events_unpinned", String(describing: p1))
|
||||
}
|
||||
/// You unpinned a message
|
||||
internal static var stateEventRoomPinnedEventsUnpinnedByYou: String { return L10n.tr("Localizable", "state_event_room_pinned_events_unpinned_by_you") }
|
||||
/// %1$@ rejected the invitation
|
||||
internal static func stateEventRoomReject(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "state_event_room_reject", String(describing: p1))
|
||||
|
||||
26
ElementX/Sources/Mocks/NetworkMonitorMock.swift
Normal file
26
ElementX/Sources/Mocks/NetworkMonitorMock.swift
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// Copyright 2024 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
|
||||
|
||||
extension NetworkMonitorMock {
|
||||
static var `default`: NetworkMonitorMock {
|
||||
let mock = NetworkMonitorMock()
|
||||
mock.underlyingReachabilityPublisher = .init(.init(.reachable))
|
||||
return mock
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol {
|
||||
mediaPlayerProvider: parameters.mediaPlayerProvider,
|
||||
voiceMessageMediaManager: parameters.voiceMessageMediaManager,
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: parameters.appMediator,
|
||||
appSettings: parameters.appSettings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -177,10 +177,10 @@ struct RoomScreenViewState: BindableState {
|
||||
// It's updated from the room info, so it's faster than using the timeline
|
||||
var pinnedEventIDs: Set<String> = []
|
||||
// This is used to control the banner
|
||||
var pinnedEventsState = PinnedEventsState()
|
||||
var pinnedEventsBannerState: PinnedEventsBannerState = .loading(numbersOfEvents: 0)
|
||||
|
||||
var shouldShowPinnedEventsBanner: Bool {
|
||||
isPinningEnabled && !pinnedEventsState.pinnedEventContents.isEmpty && lastScrollDirection != .top
|
||||
isPinningEnabled && !pinnedEventsBannerState.isEmpty && lastScrollDirection != .top
|
||||
}
|
||||
|
||||
var canJoinCall = false
|
||||
@@ -306,11 +306,12 @@ struct PinnedEventsState: Equatable {
|
||||
var pinnedEventContents: OrderedDictionary<String, AttributedString> = [:] {
|
||||
didSet {
|
||||
if selectedPinEventID == nil, !pinnedEventContents.keys.isEmpty {
|
||||
// The default selected event should always be the last one.
|
||||
selectedPinEventID = pinnedEventContents.keys.last
|
||||
} else if pinnedEventContents.isEmpty {
|
||||
selectedPinEventID = nil
|
||||
} else if let selectedPinEventID, !pinnedEventContents.keys.set.contains(selectedPinEventID) {
|
||||
self.selectedPinEventID = pinnedEventContents.firstNonNil { $0.key }
|
||||
self.selectedPinEventID = pinnedEventContents.keys.last
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -326,24 +327,15 @@ struct PinnedEventsState: Equatable {
|
||||
}
|
||||
|
||||
var selectedPinContent: AttributedString {
|
||||
guard let selectedPinEventID,
|
||||
var content = pinnedEventContents[selectedPinEventID] else {
|
||||
return AttributedString()
|
||||
var content = AttributedString(" ")
|
||||
if let selectedPinEventID,
|
||||
var pinnedEventContent = pinnedEventContents[selectedPinEventID] {
|
||||
content = pinnedEventContent
|
||||
}
|
||||
content.font = .compound.bodyMD
|
||||
return content
|
||||
}
|
||||
|
||||
var bannerIndicatorDescription: AttributedString {
|
||||
let index = selectedPinIndex + 1
|
||||
let boldPlaceholder = "{bold}"
|
||||
var finalString = AttributedString(L10n.screenRoomPinnedBannerIndicatorDescription(boldPlaceholder))
|
||||
var boldString = AttributedString(L10n.screenRoomPinnedBannerIndicator(index, pinnedEventContents.count))
|
||||
boldString.bold()
|
||||
finalString.replace(boldPlaceholder, with: boldString)
|
||||
return finalString
|
||||
}
|
||||
|
||||
mutating func nextPin() {
|
||||
guard !pinnedEventContents.isEmpty else {
|
||||
return
|
||||
@@ -353,3 +345,94 @@ struct PinnedEventsState: Equatable {
|
||||
selectedPinEventID = pinnedEventContents.keys[nextIndex]
|
||||
}
|
||||
}
|
||||
|
||||
enum PinnedEventsBannerState: Equatable {
|
||||
case loading(numbersOfEvents: Int)
|
||||
case loaded(state: PinnedEventsState)
|
||||
|
||||
var isEmpty: Bool {
|
||||
switch self {
|
||||
case .loaded(let state):
|
||||
return state.pinnedEventContents.isEmpty
|
||||
case .loading(let numberOfEvents):
|
||||
return numberOfEvents == 0
|
||||
}
|
||||
}
|
||||
|
||||
var isLoading: Bool {
|
||||
switch self {
|
||||
case .loading:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var selectedPinEventID: String? {
|
||||
switch self {
|
||||
case .loaded(let state):
|
||||
return state.selectedPinEventID
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var count: Int {
|
||||
switch self {
|
||||
case .loaded(let state):
|
||||
return state.pinnedEventContents.count
|
||||
case .loading(let numberOfEvents):
|
||||
return numberOfEvents
|
||||
}
|
||||
}
|
||||
|
||||
var selectedPinIndex: Int {
|
||||
switch self {
|
||||
case .loaded(let state):
|
||||
return state.selectedPinIndex
|
||||
case .loading(let numbersOfEvents):
|
||||
// We always want the index to be the last one when loading, since is the default one.
|
||||
return numbersOfEvents - 1
|
||||
}
|
||||
}
|
||||
|
||||
var displayedMessage: AttributedString {
|
||||
switch self {
|
||||
case .loading:
|
||||
return AttributedString(L10n.screenRoomPinnedBannerLoadingDescription)
|
||||
case .loaded(let state):
|
||||
return state.selectedPinContent
|
||||
}
|
||||
}
|
||||
|
||||
var bannerIndicatorDescription: AttributedString {
|
||||
let index = selectedPinIndex + 1
|
||||
let boldPlaceholder = "{bold}"
|
||||
var finalString = AttributedString(L10n.screenRoomPinnedBannerIndicatorDescription(boldPlaceholder))
|
||||
var boldString = AttributedString(L10n.screenRoomPinnedBannerIndicator(index, count))
|
||||
boldString.bold()
|
||||
finalString.replace(boldPlaceholder, with: boldString)
|
||||
return finalString
|
||||
}
|
||||
|
||||
mutating func nextPin() {
|
||||
switch self {
|
||||
case .loaded(var state):
|
||||
state.nextPin()
|
||||
self = .loaded(state: state)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
mutating func setPinnedEventContents(_ pinnedEventContents: OrderedDictionary<String, AttributedString>) {
|
||||
switch self {
|
||||
case .loading:
|
||||
// The default selected event should always be the last one.
|
||||
self = .loaded(state: .init(pinnedEventContents: pinnedEventContents, selectedPinEventID: pinnedEventContents.keys.last))
|
||||
case .loaded(var state):
|
||||
state.pinnedEventContents = pinnedEventContents
|
||||
self = .loaded(state: state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
private let timelineController: RoomTimelineControllerProtocol
|
||||
private let mediaPlayerProvider: MediaPlayerProviderProtocol
|
||||
private let userIndicatorController: UserIndicatorControllerProtocol
|
||||
private let networkMonitor: NetworkMonitorProtocol
|
||||
private let appMediator: AppMediatorProtocol
|
||||
private let appSettings: AppSettings
|
||||
private let analyticsService: AnalyticsService
|
||||
@@ -49,6 +50,25 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
|
||||
private var paginateBackwardsTask: Task<Void, Never>?
|
||||
private var paginateForwardsTask: Task<Void, Never>?
|
||||
private var pinnedEventsTimelineProviderSetupTask: Task<Void, Never>?
|
||||
|
||||
private var pinnedEventsTimelineProvider: RoomTimelineProviderProtocol? {
|
||||
didSet {
|
||||
guard let pinnedEventsTimelineProvider else {
|
||||
return
|
||||
}
|
||||
|
||||
buildPinnedEventContent(timelineItems: pinnedEventsTimelineProvider.itemProxies)
|
||||
pinnedEventsTimelineProvider.updatePublisher
|
||||
// When pinning or unpinning an item, the timeline might return empty for a short while, so we need to debounce it to prevent weird UI behaviours like the banner disappearing
|
||||
.debounce(for: .milliseconds(100), scheduler: DispatchQueue.main)
|
||||
.sink { [weak self] updatedItems, _ in
|
||||
guard let self else { return }
|
||||
buildPinnedEventContent(timelineItems: updatedItems)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
}
|
||||
|
||||
init(roomProxy: RoomProxyProtocol,
|
||||
focussedEventID: String? = nil,
|
||||
@@ -57,6 +77,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
mediaPlayerProvider: MediaPlayerProviderProtocol,
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerProtocol,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
networkMonitor: NetworkMonitorProtocol,
|
||||
appMediator: AppMediatorProtocol,
|
||||
appSettings: AppSettings,
|
||||
analyticsService: AnalyticsService) {
|
||||
@@ -67,6 +88,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
self.analyticsService = analyticsService
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.appMediator = appMediator
|
||||
self.networkMonitor = networkMonitor
|
||||
pinnedEventStringBuilder = .pinnedEventStringBuilder(userID: roomProxy.ownUserID)
|
||||
|
||||
let voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider)
|
||||
@@ -127,22 +149,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
}
|
||||
|
||||
Task {
|
||||
guard let pinnedEventsTimelineProvider = await roomProxy.pinnedEventsTimeline?.timelineProvider else {
|
||||
return
|
||||
}
|
||||
|
||||
buildPinnedEventContent(timelineItems: pinnedEventsTimelineProvider.itemProxies)
|
||||
|
||||
pinnedEventsTimelineProvider.updatePublisher
|
||||
// When pinning or unpinning an item, the timeline might return empty for a short while, so we need to debounce it to prevent weird UI behaviours like the banner disappearing
|
||||
.debounce(for: .milliseconds(100), scheduler: DispatchQueue.main)
|
||||
.sink { [weak self] updatedItems, _ in
|
||||
guard let self else { return }
|
||||
buildPinnedEventContent(timelineItems: updatedItems)
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
setupPinnedEventsTimelineProviderIfNeeded()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
@@ -218,10 +225,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
case let .hasScrolled(direction):
|
||||
state.lastScrollDirection = direction
|
||||
case .tappedPinnedEventsBanner:
|
||||
if let eventID = state.pinnedEventsState.selectedPinEventID {
|
||||
if let eventID = state.pinnedEventsBannerState.selectedPinEventID {
|
||||
Task { await focusOnEvent(eventID: eventID) }
|
||||
}
|
||||
state.pinnedEventsState.nextPin()
|
||||
state.pinnedEventsBannerState.nextPin()
|
||||
case .viewAllPins:
|
||||
// TODO: Implement
|
||||
break
|
||||
@@ -444,27 +451,17 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
return
|
||||
}
|
||||
// If the subscription has sent a value before the Task has started it might be lost, so before entering the loop we always do an update.
|
||||
await state.pinnedEventIDs = roomProxy.pinnedEventIDs
|
||||
await updatePinnedEventIDs()
|
||||
for await _ in roomInfoSubscription.receive(on: DispatchQueue.main).values {
|
||||
guard !Task.isCancelled else {
|
||||
return
|
||||
}
|
||||
await state.pinnedEventIDs = roomProxy.pinnedEventIDs
|
||||
await updatePinnedEventIDs()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$sharePresence
|
||||
.weakAssign(to: \.state.showReadReceipts, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$viewSourceEnabled
|
||||
.weakAssign(to: \.state.isViewSourceEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$pinningEnabled
|
||||
.weakAssign(to: \.state.isPinningEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
setupAppSettingsSubscriptions()
|
||||
|
||||
roomProxy.membersPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
@@ -511,6 +508,56 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
networkMonitor.reachabilityPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] networkState in
|
||||
if networkState == .reachable {
|
||||
self?.setupPinnedEventsTimelineProviderIfNeeded()
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func setupAppSettingsSubscriptions() {
|
||||
appSettings.$sharePresence
|
||||
.weakAssign(to: \.state.showReadReceipts, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$viewSourceEnabled
|
||||
.weakAssign(to: \.state.isViewSourceEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$pinningEnabled
|
||||
.weakAssign(to: \.state.isPinningEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
private func setupPinnedEventsTimelineProviderIfNeeded() {
|
||||
guard pinnedEventsTimelineProvider == nil,
|
||||
pinnedEventsTimelineProviderSetupTask == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
pinnedEventsTimelineProviderSetupTask = Task { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
guard let pinnedEventsTimelineProvider = await roomProxy.pinnedEventsTimeline?.timelineProvider else {
|
||||
pinnedEventsTimelineProviderSetupTask = nil
|
||||
return
|
||||
}
|
||||
|
||||
self.pinnedEventsTimelineProvider = pinnedEventsTimelineProvider
|
||||
}
|
||||
}
|
||||
|
||||
private func updatePinnedEventIDs() async {
|
||||
let pinnedEventIDs = await roomProxy.pinnedEventIDs
|
||||
// Only update the loading state of the banner
|
||||
if state.pinnedEventsBannerState.isLoading {
|
||||
state.pinnedEventsBannerState = .loading(numbersOfEvents: pinnedEventIDs.count)
|
||||
}
|
||||
state.pinnedEventIDs = pinnedEventIDs
|
||||
}
|
||||
|
||||
private func setupDirectRoomSubscriptionsIfNeeded() {
|
||||
@@ -676,7 +723,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
private func buildPinnedEventContent(timelineItems: [TimelineItemProxy]) {
|
||||
var pinnedEventContents = OrderedDictionary<String, AttributedString>()
|
||||
|
||||
for item in timelineItems {
|
||||
for item in timelineItems.reversed() {
|
||||
// Only remote events are pinned
|
||||
if case let .event(event) = item,
|
||||
let eventID = event.id.eventID {
|
||||
@@ -685,7 +732,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
||||
}
|
||||
}
|
||||
|
||||
state.pinnedEventsState.pinnedEventContents = pinnedEventContents
|
||||
state.pinnedEventsBannerState.setPinnedEventContents(pinnedEventContents)
|
||||
}
|
||||
|
||||
private func buildTimelineViews(timelineItems: [RoomTimelineItemProtocol], isSwitchingTimelines: Bool = false) {
|
||||
@@ -906,6 +953,7 @@ extension RoomScreenViewModel {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -18,7 +18,7 @@ import Compound
|
||||
import SwiftUI
|
||||
|
||||
struct PinnedItemsBannerView: View {
|
||||
let pinnedEventsState: PinnedEventsState
|
||||
let state: PinnedEventsBannerState
|
||||
|
||||
let onMainButtonTap: () -> Void
|
||||
let onViewAllButtonTap: () -> Void
|
||||
@@ -38,7 +38,7 @@ struct PinnedItemsBannerView: View {
|
||||
Button { onMainButtonTap() } label: {
|
||||
HStack(spacing: 0) {
|
||||
HStack(spacing: 10) {
|
||||
PinnedItemsIndicatorView(pinIndex: pinnedEventsState.selectedPinIndex, pinsCount: pinnedEventsState.pinnedEventContents.count)
|
||||
PinnedItemsIndicatorView(pinIndex: state.selectedPinIndex, pinsCount: state.count)
|
||||
.accessibilityHidden(true)
|
||||
CompoundIcon(\.pinSolid, size: .small, relativeTo: .compound.bodyMD)
|
||||
.foregroundColor(Color.compound.iconSecondaryAlpha)
|
||||
@@ -48,26 +48,34 @@ struct PinnedItemsBannerView: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.disabled(state.isLoading)
|
||||
.accessibilityElement(children: .contain)
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var viewAllButton: some View {
|
||||
Button { onViewAllButtonTap() } label: {
|
||||
Text(L10n.screenRoomPinnedBannerViewAllButtonTitle)
|
||||
.font(.compound.bodyMDSemibold)
|
||||
.foregroundStyle(Color.compound.textPrimary)
|
||||
switch state {
|
||||
case .loaded:
|
||||
Button { onViewAllButtonTap() } label: {
|
||||
Text(L10n.screenRoomPinnedBannerViewAllButtonTitle)
|
||||
.font(.compound.bodyMDSemibold)
|
||||
.foregroundStyle(Color.compound.textPrimary)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
case .loading:
|
||||
ProgressView()
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 5)
|
||||
}
|
||||
}
|
||||
|
||||
private var content: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(pinnedEventsState.bannerIndicatorDescription)
|
||||
Text(state.bannerIndicatorDescription)
|
||||
.font(.compound.bodySM)
|
||||
.foregroundColor(.compound.textActionAccent)
|
||||
.lineLimit(1)
|
||||
Text(pinnedEventsState.selectedPinContent)
|
||||
Text(state.displayedMessage)
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.lineLimit(1)
|
||||
@@ -79,27 +87,30 @@ struct PinnedItemsBannerView_Previews: PreviewProvider, TestablePreview {
|
||||
static var attributedContent: AttributedString {
|
||||
var boldPart = AttributedString("Image:")
|
||||
boldPart.bold()
|
||||
var final = boldPart + " content.png"
|
||||
var finalString = boldPart + " content.png"
|
||||
// This should be ignored when presented
|
||||
final.font = .headline
|
||||
return final
|
||||
finalString.font = .headline
|
||||
return finalString
|
||||
}
|
||||
|
||||
static var previews: some View {
|
||||
VStack(spacing: 20) {
|
||||
PinnedItemsBannerView(pinnedEventsState: .init(pinnedEventContents: ["1": "Content",
|
||||
"2": "2",
|
||||
"3": "3"],
|
||||
selectedPinEventID: "1"),
|
||||
PinnedItemsBannerView(state: .loaded(state: .init(pinnedEventContents: ["1": "Content",
|
||||
"2": "2",
|
||||
"3": "3"],
|
||||
selectedPinEventID: "1")),
|
||||
onMainButtonTap: { },
|
||||
onViewAllButtonTap: { })
|
||||
PinnedItemsBannerView(pinnedEventsState: .init(pinnedEventContents: ["1": "Very very very very long content here",
|
||||
"2": "2"],
|
||||
selectedPinEventID: "1"),
|
||||
PinnedItemsBannerView(state: .loaded(state: .init(pinnedEventContents: ["1": "Very very very very long content here",
|
||||
"2": "2"],
|
||||
selectedPinEventID: "1")),
|
||||
onMainButtonTap: { },
|
||||
onViewAllButtonTap: { })
|
||||
PinnedItemsBannerView(pinnedEventsState: .init(pinnedEventContents: ["1": attributedContent],
|
||||
selectedPinEventID: "1"),
|
||||
PinnedItemsBannerView(state: .loaded(state: .init(pinnedEventContents: ["1": attributedContent],
|
||||
selectedPinEventID: "1")),
|
||||
onMainButtonTap: { },
|
||||
onViewAllButtonTap: { })
|
||||
PinnedItemsBannerView(state: .loading(numbersOfEvents: 5),
|
||||
onMainButtonTap: { },
|
||||
onViewAllButtonTap: { })
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: UserIndicatorControllerMock(),
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -110,7 +110,7 @@ struct RoomScreen: View {
|
||||
}
|
||||
|
||||
private var pinnedItemsBanner: some View {
|
||||
PinnedItemsBannerView(pinnedEventsState: context.viewState.pinnedEventsState,
|
||||
PinnedItemsBannerView(state: context.viewState.pinnedEventsBannerState,
|
||||
onMainButtonTap: { context.send(viewAction: .tappedPinnedEventsBanner) },
|
||||
onViewAllButtonTap: { context.send(viewAction: .viewAllPins) })
|
||||
.transition(.move(edge: .top))
|
||||
@@ -213,6 +213,7 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -96,6 +96,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -101,6 +101,7 @@ struct HighlightedTimelineItemTimeline_Previews: PreviewProvider {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -86,6 +86,7 @@ struct TimelineView_Previews: PreviewProvider, TestablePreview {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -42,6 +42,11 @@ struct RoomEventStringBuilder {
|
||||
case .redactedMessage:
|
||||
return prefix(L10n.commonMessageRemoved, with: displayName)
|
||||
case .sticker:
|
||||
if messageEventStringBuilder.prefix == .messageType {
|
||||
var string = AttributedString(L10n.commonSticker)
|
||||
string.bold()
|
||||
return string
|
||||
}
|
||||
return prefix(L10n.commonSticker, with: displayName)
|
||||
case .failedToParseMessageLike, .failedToParseState:
|
||||
return prefix(L10n.commonUnsupportedEvent, with: displayName)
|
||||
@@ -70,6 +75,14 @@ struct RoomEventStringBuilder {
|
||||
memberIsYou: isOutgoing)
|
||||
.map(AttributedString.init)
|
||||
case .poll(let question, _, _, _, _, _, _):
|
||||
if messageEventStringBuilder.prefix == .messageType {
|
||||
let questionPlaceholder = "{question}"
|
||||
var finalString = AttributedString(L10n.commonPollSummary(questionPlaceholder))
|
||||
finalString.bold()
|
||||
let normalString = AttributedString(question)
|
||||
finalString.replace(questionPlaceholder, with: normalString)
|
||||
return finalString
|
||||
}
|
||||
return prefix(L10n.commonPollSummary(question), with: displayName)
|
||||
case .callInvite:
|
||||
return prefix(L10n.commonCallInvite, with: displayName)
|
||||
@@ -95,7 +108,7 @@ struct RoomEventStringBuilder {
|
||||
RoomEventStringBuilder(stateEventStringBuilder: .init(userID: userID,
|
||||
shouldDisambiguateDisplayNames: false),
|
||||
messageEventStringBuilder: .init(attributedStringBuilder: AttributedStringBuilder(cacheKey: "pinnedEvents", mentionBuilder: PlainMentionBuilder()),
|
||||
prefix: .mediaType),
|
||||
prefix: .messageType),
|
||||
shouldDisambiguateDisplayNames: false,
|
||||
shouldPrefixSenderName: false)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import MatrixRustSDK
|
||||
struct RoomMessageEventStringBuilder {
|
||||
enum Prefix {
|
||||
case senderName
|
||||
case mediaType
|
||||
case messageType
|
||||
case none
|
||||
}
|
||||
|
||||
@@ -41,19 +41,19 @@ struct RoomMessageEventStringBuilder {
|
||||
case .audio(content: let content):
|
||||
let isVoiceMessage = content.voice != nil
|
||||
var content = AttributedString(isVoiceMessage ? L10n.commonVoiceMessage : L10n.commonAudio)
|
||||
if prefix == .mediaType {
|
||||
if prefix == .messageType {
|
||||
content.bold()
|
||||
}
|
||||
message = content
|
||||
case .image(let content):
|
||||
message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonImage) : AttributedString("\(L10n.commonImage) - \(content.body)")
|
||||
message = prefix == .messageType ? prefix(AttributedString(content.body), with: L10n.commonImage) : AttributedString("\(L10n.commonImage) - \(content.body)")
|
||||
case .video(let content):
|
||||
message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonVideo) : AttributedString("\(L10n.commonVideo) - \(content.body)")
|
||||
message = prefix == .messageType ? prefix(AttributedString(content.body), with: L10n.commonVideo) : AttributedString("\(L10n.commonVideo) - \(content.body)")
|
||||
case .file(let content):
|
||||
message = prefix == .mediaType ? prefix(AttributedString(content.body), with: L10n.commonFile) : AttributedString("\(L10n.commonFile) - \(content.body)")
|
||||
message = prefix == .messageType ? prefix(AttributedString(content.body), with: L10n.commonFile) : AttributedString("\(L10n.commonFile) - \(content.body)")
|
||||
case .location:
|
||||
var content = AttributedString(L10n.commonSharedLocation)
|
||||
if prefix == .mediaType {
|
||||
if prefix == .messageType {
|
||||
content.bold()
|
||||
}
|
||||
message = content
|
||||
|
||||
@@ -47,6 +47,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate
|
||||
AppSettings.resetAllSettings()
|
||||
ServiceLocator.shared.register(appSettings: AppSettings())
|
||||
ServiceLocator.shared.register(bugReportService: BugReportServiceMock())
|
||||
ServiceLocator.shared.register(networkMonitor: NetworkMonitorMock.default)
|
||||
|
||||
let analyticsClient = AnalyticsClientMock()
|
||||
analyticsClient.isRunning = false
|
||||
|
||||
@@ -27,6 +27,7 @@ class UnitTestsAppCoordinator: AppCoordinatorProtocol {
|
||||
AppSettings.resetAllSettings()
|
||||
ServiceLocator.shared.register(appSettings: AppSettings())
|
||||
ServiceLocator.shared.register(bugReportService: BugReportServiceMock())
|
||||
ServiceLocator.shared.register(networkMonitor: NetworkMonitorMock.default)
|
||||
|
||||
let analyticsClient = AnalyticsClientMock()
|
||||
analyticsClient.isRunning = false
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5eb75e371a78b17fad61215006b871310665bf8061772985d047facbccc5bae7
|
||||
size 129176
|
||||
oid sha256:52a543349c38a46a7abf42d9f5b83aed47b40621e358fe63b98487acff03b461
|
||||
size 147075
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:745d8fdea278dc6ab55f39501384ecc5efc3899db233c52c797a8542f4885adc
|
||||
size 154132
|
||||
oid sha256:131dadbb128453abb744bd49c2cd592bbd9f0f5bdee5ba6ea71f541a10ee70ee
|
||||
size 182373
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:82dc33b9de9b497388b91304afe2623e4fa5c9d5aff246a87b68c4d948a63a37
|
||||
size 76715
|
||||
oid sha256:ade81f08af5344aab81fdebbffe27a4b04976ce8e3cc8f397d5328c51998ad09
|
||||
size 91413
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:48df9215b99d69572bf3c4395e4b13d0b34012691b98bba75c73fc35c3b0b6fb
|
||||
size 85989
|
||||
oid sha256:919e3df86ce263d06f479c701b6981a66b0d6217e13cf3cff12445b9c6a24d77
|
||||
size 106938
|
||||
|
||||
@@ -32,6 +32,7 @@ class PillContextTests: XCTestCase {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
@@ -60,6 +61,7 @@ class PillContextTests: XCTestCase {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
@@ -81,6 +83,7 @@ class PillContextTests: XCTestCase {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: ServiceLocator.shared.userIndicatorController,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
133
UnitTests/Sources/PinnedEventsBannerStateTests.swift
Normal file
133
UnitTests/Sources/PinnedEventsBannerStateTests.swift
Normal file
@@ -0,0 +1,133 @@
|
||||
//
|
||||
// Copyright 2024 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 XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
class PinnedEventsBannerStateTests: XCTestCase {
|
||||
func testEmpty() {
|
||||
var state = PinnedEventsBannerState.loading(numbersOfEvents: 0)
|
||||
XCTAssertTrue(state.isEmpty)
|
||||
|
||||
state = .loaded(state: .init())
|
||||
XCTAssertTrue(state.isEmpty)
|
||||
}
|
||||
|
||||
func testLoading() {
|
||||
let originalState = PinnedEventsBannerState.loading(numbersOfEvents: 5)
|
||||
|
||||
var state = originalState
|
||||
// This should not affect the state when loading
|
||||
state.nextPin()
|
||||
XCTAssertEqual(state, originalState)
|
||||
|
||||
XCTAssertTrue(state.isLoading)
|
||||
XCTAssertFalse(state.isEmpty)
|
||||
XCTAssertNil(state.selectedPinEventID)
|
||||
XCTAssertEqual(state.displayedMessage.string, L10n.screenRoomPinnedBannerLoadingDescription)
|
||||
XCTAssertEqual(state.selectedPinIndex, 4)
|
||||
XCTAssertEqual(state.count, 5)
|
||||
XCTAssertEqual(state.bannerIndicatorDescription.string, L10n.screenRoomPinnedBannerIndicatorDescription(L10n.screenRoomPinnedBannerIndicator(5, 5)))
|
||||
}
|
||||
|
||||
func testLoadingToLoaded() {
|
||||
var state = PinnedEventsBannerState.loading(numbersOfEvents: 2)
|
||||
XCTAssertTrue(state.isLoading)
|
||||
state.setPinnedEventContents(["1": "test1", "2": "test2"])
|
||||
XCTAssertEqual(state, .loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2"], selectedPinEventID: "2")))
|
||||
XCTAssertFalse(state.isLoading)
|
||||
}
|
||||
|
||||
func testLoaded() {
|
||||
let state = PinnedEventsBannerState.loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2"], selectedPinEventID: "2"))
|
||||
XCTAssertFalse(state.isLoading)
|
||||
XCTAssertFalse(state.isEmpty)
|
||||
XCTAssertEqual(state.selectedPinEventID, "2")
|
||||
XCTAssertEqual(state.displayedMessage.string, "test2")
|
||||
XCTAssertEqual(state.selectedPinIndex, 1)
|
||||
XCTAssertEqual(state.count, 2)
|
||||
XCTAssertEqual(state.bannerIndicatorDescription.string, L10n.screenRoomPinnedBannerIndicatorDescription(L10n.screenRoomPinnedBannerIndicator(2, 2)))
|
||||
}
|
||||
|
||||
func testNextPin() {
|
||||
var state = PinnedEventsBannerState.loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2", "3": "test3"], selectedPinEventID: "3"))
|
||||
XCTAssertEqual(state.selectedPinEventID, "3")
|
||||
XCTAssertEqual(state.selectedPinIndex, 2)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test3")
|
||||
|
||||
state.nextPin()
|
||||
XCTAssertEqual(state.selectedPinEventID, "1")
|
||||
XCTAssertEqual(state.selectedPinIndex, 0)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test1")
|
||||
|
||||
state.nextPin()
|
||||
XCTAssertEqual(state.selectedPinEventID, "2")
|
||||
XCTAssertEqual(state.selectedPinIndex, 1)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test2")
|
||||
}
|
||||
|
||||
func testSetContent() {
|
||||
var state = PinnedEventsBannerState.loaded(state: .init(pinnedEventContents: ["1": "test1", "2": "test2", "3": "test3", "4": "test4"], selectedPinEventID: "2"))
|
||||
XCTAssertEqual(state.selectedPinEventID, "2")
|
||||
XCTAssertEqual(state.selectedPinIndex, 1)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test2")
|
||||
XCTAssertEqual(state.count, 4)
|
||||
XCTAssertFalse(state.isEmpty)
|
||||
|
||||
// let's remove the selected item
|
||||
state.setPinnedEventContents(["1": "test1", "3": "test3", "4": "test4"])
|
||||
// new selected item is the new latest
|
||||
XCTAssertEqual(state.selectedPinEventID, "4")
|
||||
XCTAssertEqual(state.selectedPinIndex, 2)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test4")
|
||||
XCTAssertEqual(state.count, 3)
|
||||
XCTAssertFalse(state.isEmpty)
|
||||
|
||||
// let's add a new item at the top
|
||||
state.setPinnedEventContents(["0": "test0", "1": "test1", "3": "test3", "4": "test4"])
|
||||
// selected item doesn't change
|
||||
XCTAssertEqual(state.selectedPinEventID, "4")
|
||||
// but the index is updated
|
||||
XCTAssertEqual(state.selectedPinIndex, 3)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test4")
|
||||
XCTAssertEqual(state.count, 4)
|
||||
XCTAssertFalse(state.isEmpty)
|
||||
|
||||
// let's add a new item at the bottom
|
||||
state.setPinnedEventContents(["0": "test0", "1": "test1", "3": "test3", "4": "test4", "5": "test5"])
|
||||
// selected item doesn't change
|
||||
XCTAssertEqual(state.selectedPinEventID, "4")
|
||||
// and index stays the same
|
||||
XCTAssertEqual(state.selectedPinIndex, 3)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test4")
|
||||
XCTAssertEqual(state.count, 5)
|
||||
XCTAssertFalse(state.isEmpty)
|
||||
|
||||
// set to tempty
|
||||
state.setPinnedEventContents([:])
|
||||
XCTAssertTrue(state.isEmpty)
|
||||
XCTAssertNil(state.selectedPinEventID)
|
||||
|
||||
// set to one item
|
||||
state.setPinnedEventContents(["6": "test6", "7": "test7"])
|
||||
XCTAssertEqual(state.selectedPinEventID, "7")
|
||||
XCTAssertEqual(state.selectedPinIndex, 1)
|
||||
XCTAssertEqual(state.displayedMessage.string, "test7")
|
||||
XCTAssertEqual(state.count, 2)
|
||||
XCTAssertFalse(state.isEmpty)
|
||||
}
|
||||
}
|
||||
@@ -351,6 +351,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
@@ -375,6 +376,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
@@ -399,6 +401,7 @@ class RoomScreenViewModelTests: XCTestCase {
|
||||
mediaPlayerProvider: MediaPlayerProviderMock(),
|
||||
voiceMessageMediaManager: VoiceMessageMediaManagerMock(),
|
||||
userIndicatorController: userIndicatorControllerMock,
|
||||
networkMonitor: ServiceLocator.shared.networkMonitor,
|
||||
appMediator: AppMediatorMock.default,
|
||||
appSettings: ServiceLocator.shared.settings,
|
||||
analyticsService: ServiceLocator.shared.analytics)
|
||||
|
||||
@@ -60,7 +60,7 @@ packages:
|
||||
# Element/Matrix dependencies
|
||||
MatrixRustSDK:
|
||||
url: https://github.com/element-hq/matrix-rust-components-swift
|
||||
exactVersion: 1.0.31
|
||||
exactVersion: 1.0.32
|
||||
# path: ../matrix-rust-sdk
|
||||
Compound:
|
||||
url: https://github.com/element-hq/compound-ios
|
||||
|
||||
Reference in New Issue
Block a user