Fixes #2360 - Add support for manually marking rooms as unread (#2408)

* Fixes #2360 - Add support for manually marking rooms as unread

* Add analytics for marking rooms as (un)read

* Remove unnecessary analytics abstraction levels

* Fix breaking API changes following SDK bump

* Add changelog

* Handle PR comments

* Fix unit tests
This commit is contained in:
Stefan Ceriu
2024-02-05 18:06:50 +02:00
committed by GitHub
parent e80612b968
commit 9915a33964
46 changed files with 250 additions and 305 deletions
+10 -11
View File
@@ -144,7 +144,6 @@
22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; }; 22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CEBE5EA91E8691EDF364EC2 /* UITestsScreenIdentifier.swift */; };
234E2C782981003971ABE96E /* PermalinkBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */; }; 234E2C782981003971ABE96E /* PermalinkBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F754E66A8970963B15B2A41E /* PermalinkBuilder.swift */; };
2352C541AF857241489756FF /* MockRoomSummaryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */; }; 2352C541AF857241489756FF /* MockRoomSummaryProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F7D42E66E939B709C1EC390 /* MockRoomSummaryProvider.swift */; };
2355289BB0146231DD8AFFC0 /* AnalyticsMessageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2133A5FF0C14986E60326115 /* AnalyticsMessageType.swift */; };
23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE203026B9AD3DB412439866 /* MediaUploadingPreprocessorTests.swift */; }; 23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE203026B9AD3DB412439866 /* MediaUploadingPreprocessorTests.swift */; };
237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; }; 237FC70AA257B935F53316BA /* SessionVerificationControllerProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55D7E514F9DE4E3D72FDCAD /* SessionVerificationControllerProxy.swift */; };
245F7FE5961BD10C145A26E0 /* UITimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA689E792E679F5E3956F21 /* UITimelineView.swift */; }; 245F7FE5961BD10C145A26E0 /* UITimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EA689E792E679F5E3956F21 /* UITimelineView.swift */; };
@@ -838,7 +837,6 @@
D1E29F345F1220E1AF1BE9DF /* ReadReceiptsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB0A77874B29D79DDFC051AC /* ReadReceiptsSummaryView.swift */; }; D1E29F345F1220E1AF1BE9DF /* ReadReceiptsSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB0A77874B29D79DDFC051AC /* ReadReceiptsSummaryView.swift */; };
D1EEF0CB0F5D9C15E224E670 /* landscape_test_video.mov in Resources */ = {isa = PBXBuildFile; fileRef = 9A2AC7BE17C05CF7D2A22338 /* landscape_test_video.mov */; }; D1EEF0CB0F5D9C15E224E670 /* landscape_test_video.mov in Resources */ = {isa = PBXBuildFile; fileRef = 9A2AC7BE17C05CF7D2A22338 /* landscape_test_video.mov */; };
D2048FD56760BDABA3DB5FC2 /* AppLockServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */; }; D2048FD56760BDABA3DB5FC2 /* AppLockServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 26EAAB54C6CE91D64B69A9F8 /* AppLockServiceProtocol.swift */; };
D2A15D03F81342A09340BD56 /* AnalyticsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */; };
D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 033DB41C51865A2E83174E87 /* target.yml */; }; D2D70B5DB1A5E4AF0CD88330 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = 033DB41C51865A2E83174E87 /* target.yml */; };
D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */; }; D33AC79A50DFC26D2498DD28 /* FileRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5098DA7799946A61E34A2373 /* FileRoomTimelineItem.swift */; };
D34E328E9E65904358248FDD /* GlobalSearchScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */; }; D34E328E9E65904358248FDD /* GlobalSearchScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436A0D98D372B17EAE9AA999 /* GlobalSearchScreenModels.swift */; };
@@ -1192,7 +1190,6 @@
1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItemContent.swift; sourceTree = "<group>"; }; 1FD51B4D5173F7FC886F5360 /* NoticeRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineItemContent.swift; sourceTree = "<group>"; };
201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = "<group>"; }; 201305507D7DFD16E544563A /* EmojiLoaderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiLoaderProtocol.swift; sourceTree = "<group>"; };
20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMapProtocol.swift; sourceTree = "<group>"; }; 20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStaticMapProtocol.swift; sourceTree = "<group>"; };
2133A5FF0C14986E60326115 /* AnalyticsMessageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsMessageType.swift; sourceTree = "<group>"; };
2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; }; 2141693488CE5446BB391964 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItemContent.swift; sourceTree = "<group>"; }; 216F0DDC98F2A2C162D09C28 /* FileRoomTimelineItemContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileRoomTimelineItemContent.swift; sourceTree = "<group>"; };
218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = "<group>"; }; 218AB05B4E3889731959C5F1 /* EventBasedTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineItemProtocol.swift; sourceTree = "<group>"; };
@@ -1266,6 +1263,8 @@
340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModelProtocol.swift; sourceTree = "<group>"; }; 340179A0FC1AD4AEDA7FC134 /* CreateRoomViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateRoomViewModelProtocol.swift; sourceTree = "<group>"; };
342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 342BEBC3C5FC3F9943C41C4C /* TemplateScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelProtocol.swift; sourceTree = "<group>"; };
347D708104CCEF771428C9A3 /* PollFormScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModelTests.swift; sourceTree = "<group>"; }; 347D708104CCEF771428C9A3 /* PollFormScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenViewModelTests.swift; sourceTree = "<group>"; };
34E0FA38BD473FFA6F1AB7A5 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = be; path = be.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
34ED3AB7E0287552A5648AB3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/InfoPlist.strings; sourceTree = "<group>"; };
351E89CE2ED9B73C5CC47955 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = "<group>"; }; 351E89CE2ED9B73C5CC47955 /* TimelineReactionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineReactionsView.swift; sourceTree = "<group>"; };
3558A15CFB934F9229301527 /* RestorationToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestorationToken.swift; sourceTree = "<group>"; }; 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>"; }; 35AFCF4C05DEED04E3DB1A16 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -1945,6 +1944,7 @@
EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = "<group>"; }; EFF7BF82A950B91BC5469E91 /* ViewFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewFrameReader.swift; sourceTree = "<group>"; };
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceTests.swift; sourceTree = "<group>"; }; EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BugReportServiceTests.swift; sourceTree = "<group>"; };
F012CB5EE3F2B67359F6CC52 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; }; F012CB5EE3F2B67359F6CC52 /* target.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = target.yml; sourceTree = "<group>"; };
F0205C03F98BE861EDABCB0D /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Localizable.strings; sourceTree = "<group>"; };
F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; }; F08776C48FFB47CACF64ED10 /* ServerConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; };
F0B9F5BC4C80543DE7228B9D /* MapTilerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyle.swift; sourceTree = "<group>"; }; F0B9F5BC4C80543DE7228B9D /* MapTilerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTilerStyle.swift; sourceTree = "<group>"; };
F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModel.swift; sourceTree = "<group>"; }; F0E14FF533D25A0692F7CEB0 /* RoomPollsHistoryScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenViewModel.swift; sourceTree = "<group>"; };
@@ -1994,7 +1994,6 @@
FDF73F49E6B6683F7E2D26F0 /* SecureBackupScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenCoordinator.swift; sourceTree = "<group>"; }; FDF73F49E6B6683F7E2D26F0 /* SecureBackupScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreenCoordinator.swift; sourceTree = "<group>"; };
FE87C931165F5E201CACBB87 /* MediaPlayerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProtocol.swift; sourceTree = "<group>"; }; FE87C931165F5E201CACBB87 /* MediaPlayerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayerProtocol.swift; sourceTree = "<group>"; };
FEC2E8E1B20BB2EA07B0B61E /* WelcomeScreenScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenViewModel.swift; sourceTree = "<group>"; }; FEC2E8E1B20BB2EA07B0B61E /* WelcomeScreenScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeScreenScreenViewModel.swift; sourceTree = "<group>"; };
FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsScreen.swift; sourceTree = "<group>"; };
FFECCE59967018204876D0A5 /* LocationMarkerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMarkerView.swift; sourceTree = "<group>"; }; FFECCE59967018204876D0A5 /* LocationMarkerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMarkerView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@@ -2932,8 +2931,6 @@
E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */, E3B97591B2D3D4D67553506D /* AnalyticsClientProtocol.swift */,
D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */, D77B3D4950F1707E66E4A45A /* AnalyticsConfiguration.swift */,
57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */, 57B6B383F1FD04CC0E7B60C6 /* AnalyticsConsentState.swift */,
2133A5FF0C14986E60326115 /* AnalyticsMessageType.swift */,
FEFEEE93B82937B2E86F92EB /* AnalyticsScreen.swift */,
5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */, 5445FCE0CE15E634FDC1A2E2 /* AnalyticsService.swift */,
A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */, A6B891A6DA826E2461DBB40F /* PHGPostHogConfiguration.swift */,
1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */, 1715E3D7F53C0748AA50C91C /* PostHogAnalyticsClient.swift */,
@@ -4979,6 +4976,7 @@
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
knownRegions = ( knownRegions = (
Base, Base,
be,
cs, cs,
de, de,
en, en,
@@ -5388,13 +5386,11 @@
F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */, F7567DD6635434E8C563BF85 /* AnalyticsClientProtocol.swift in Sources */,
54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */, 54C774874BED4A8FAD1F22FE /* AnalyticsConfiguration.swift in Sources */,
8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */, 8DDC6F28C797D8685F2F8E32 /* AnalyticsConsentState.swift in Sources */,
2355289BB0146231DD8AFFC0 /* AnalyticsMessageType.swift in Sources */,
9DF3F6318A4402305F5EB869 /* AnalyticsPromptScreen.swift in Sources */, 9DF3F6318A4402305F5EB869 /* AnalyticsPromptScreen.swift in Sources */,
5F28C9146694B381BB82E18C /* AnalyticsPromptScreenCoordinator.swift in Sources */, 5F28C9146694B381BB82E18C /* AnalyticsPromptScreenCoordinator.swift in Sources */,
496CC9D59ACFAB84FD9B3B5F /* AnalyticsPromptScreenModels.swift in Sources */, 496CC9D59ACFAB84FD9B3B5F /* AnalyticsPromptScreenModels.swift in Sources */,
0AA0477E063E72B786A983CF /* AnalyticsPromptScreenViewModel.swift in Sources */, 0AA0477E063E72B786A983CF /* AnalyticsPromptScreenViewModel.swift in Sources */,
D4ACF3276F5D0DA28D4028C9 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */, D4ACF3276F5D0DA28D4028C9 /* AnalyticsPromptScreenViewModelProtocol.swift in Sources */,
D2A15D03F81342A09340BD56 /* AnalyticsScreen.swift in Sources */,
3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */, 3C73442084BF8A6939F0F80B /* AnalyticsService.swift in Sources */,
020F7E70167FB2833266F2F0 /* AnalyticsSettingsScreen.swift in Sources */, 020F7E70167FB2833266F2F0 /* AnalyticsSettingsScreen.swift in Sources */,
95690DDD9D547D3D842ACBE3 /* AnalyticsSettingsScreenCoordinator.swift in Sources */, 95690DDD9D547D3D842ACBE3 /* AnalyticsSettingsScreenCoordinator.swift in Sources */,
@@ -6190,6 +6186,7 @@
187853A7E643995EE49FAD43 /* Localizable.stringsdict */ = { 187853A7E643995EE49FAD43 /* Localizable.stringsdict */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
34E0FA38BD473FFA6F1AB7A5 /* be */,
6654859746B0BE9611459391 /* cs */, 6654859746B0BE9611459391 /* cs */,
AE5DDBEBBA17973ED4638823 /* de */, AE5DDBEBBA17973ED4638823 /* de */,
13802897C7AFA360EA74C0B0 /* en */, 13802897C7AFA360EA74C0B0 /* en */,
@@ -6209,6 +6206,7 @@
7109E709A7738E6BCC4553E6 /* Localizable.strings */ = { 7109E709A7738E6BCC4553E6 /* Localizable.strings */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
F0205C03F98BE861EDABCB0D /* be */,
8D8169443E5AC5FF71BFB3DB /* cs */, 8D8169443E5AC5FF71BFB3DB /* cs */,
35AFCF4C05DEED04E3DB1A16 /* de */, 35AFCF4C05DEED04E3DB1A16 /* de */,
CACA846B3E3E9A521D98B178 /* en */, CACA846B3E3E9A521D98B178 /* en */,
@@ -6228,6 +6226,7 @@
91DE43B8815918E590912DDA /* InfoPlist.strings */ = { 91DE43B8815918E590912DDA /* InfoPlist.strings */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
children = ( children = (
34ED3AB7E0287552A5648AB3 /* be */,
8D1FA20DAB853C1156054912 /* cs */, 8D1FA20DAB853C1156054912 /* cs */,
84311D707B09854D67F78BBF /* de */, 84311D707B09854D67F78BBF /* de */,
1215A4FC53D2319E81AE8970 /* en */, 1215A4FC53D2319E81AE8970 /* en */,
@@ -6764,7 +6763,7 @@
repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift"; repositoryURL = "https://github.com/matrix-org/matrix-rust-components-swift";
requirement = { requirement = {
kind = exactVersion; kind = exactVersion;
version = 1.1.36; version = 1.1.37;
}; };
}; };
821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = { 821C67C9A7F8CC3FD41B28B4 /* XCRemoteSwiftPackageReference "emojibase-bindings" */ = {
@@ -6811,8 +6810,8 @@
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/matrix-org/matrix-analytics-events"; repositoryURL = "https://github.com/matrix-org/matrix-analytics-events";
requirement = { requirement = {
kind = upToNextMinorVersion; kind = revision;
minimumVersion = 0.9.0; revision = 28d1e65d306420b3affc32f719cb516f74a7406f;
}; };
}; };
C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = { C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */ = {
@@ -121,8 +121,7 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-analytics-events", "location" : "https://github.com/matrix-org/matrix-analytics-events",
"state" : { "state" : {
"revision" : "aa14cbcdf81af2746d20a71779ec751f971e1d7f", "revision" : "28d1e65d306420b3affc32f719cb516f74a7406f"
"version" : "0.9.0"
} }
}, },
{ {
@@ -130,8 +129,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" : "e6aecad747a12e399ee718b5fb2ece45b7b6e23a", "revision" : "4d0d75004f8361530d7424ab198e363027823718",
"version" : "1.1.36" "version" : "1.1.37"
} }
}, },
{ {
@@ -47,6 +47,7 @@ final class AppSettings {
case userSuggestionsEnabled case userSuggestionsEnabled
case mentionsBadgeEnabled case mentionsBadgeEnabled
case roomListFiltersEnabled case roomListFiltersEnabled
case markAsUnreadEnabled
} }
private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
@@ -275,6 +276,9 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.roomListFiltersEnabled, defaultValue: false, storageType: .userDefaults(store)) @UserPreference(key: UserDefaultsKeys.roomListFiltersEnabled, defaultValue: false, storageType: .userDefaults(store))
var roomListFiltersEnabled var roomListFiltersEnabled
@UserPreference(key: UserDefaultsKeys.markAsUnreadEnabled, defaultValue: false, storageType: .userDefaults(store))
var markAsUnreadEnabled
#endif #endif
// MARK: - Shared // MARK: - Shared
@@ -417,6 +417,12 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
self.roomProxy = roomProxy self.roomProxy = roomProxy
Task {
// Mark the room as read on entering but don't send read receipts
// as those will be handled by the timeline
await roomProxy.markAsRead(sendReadReceipts: false, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate)
}
let userID = userSession.clientProxy.userID let userID = userSession.clientProxy.userID
let timelineItemFactory = RoomTimelineItemFactory(userID: userID, let timelineItemFactory = RoomTimelineItemFactory(userID: userID,
@@ -772,7 +778,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
self.analytics.trackComposer(inThread: false, self.analytics.trackComposer(inThread: false,
isEditing: false, isEditing: false,
isReply: false, isReply: false,
messageType: isUserLocation ? .location(.user) : .location(.pin), messageType: isUserLocation ? .LocationUser : .LocationPin,
startsThread: nil) startsThread: nil)
case .close: case .close:
self.navigationSplitCoordinator.setSheetCoordinator(nil) self.navigationSplitCoordinator.setSheetCoordinator(nil)
@@ -833,7 +839,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
self.analytics.trackComposer(inThread: false, self.analytics.trackComposer(inThread: false,
isEditing: false, isEditing: false,
isReply: false, isReply: false,
messageType: .poll, messageType: .Poll,
startsThread: nil) startsThread: nil)
self.analytics.trackPollCreated(isUndisclosed: pollKind == .undisclosed, numberOfAnswers: options.count) self.analytics.trackPollCreated(isUndisclosed: pollKind == .undisclosed, numberOfAnswers: options.count)
@@ -1847,11 +1847,6 @@ class RoomProxyMock: RoomProxyProtocol {
} }
var underlyingHasOngoingCall: Bool! var underlyingHasOngoingCall: Bool!
var canonicalAlias: String? var canonicalAlias: String?
var hasUnreadNotifications: Bool {
get { return underlyingHasUnreadNotifications }
set(value) { underlyingHasUnreadNotifications = value }
}
var underlyingHasUnreadNotifications: Bool!
var ownUserID: String { var ownUserID: String {
get { return underlyingOwnUserID } get { return underlyingOwnUserID }
set(value) { underlyingOwnUserID = value } set(value) { underlyingOwnUserID = value }
@@ -2222,6 +2217,44 @@ class RoomProxyMock: RoomProxyProtocol {
return canUserTriggerRoomNotificationUserIDReturnValue return canUserTriggerRoomNotificationUserIDReturnValue
} }
} }
//MARK: - markAsUnread
var markAsUnreadCallsCount = 0
var markAsUnreadCalled: Bool {
return markAsUnreadCallsCount > 0
}
var markAsUnreadReturnValue: Result<Void, RoomProxyError>!
var markAsUnreadClosure: (() async -> Result<Void, RoomProxyError>)?
func markAsUnread() async -> Result<Void, RoomProxyError> {
markAsUnreadCallsCount += 1
if let markAsUnreadClosure = markAsUnreadClosure {
return await markAsUnreadClosure()
} else {
return markAsUnreadReturnValue
}
}
//MARK: - markAsRead
var markAsReadSendReadReceiptsReceiptTypeCallsCount = 0
var markAsReadSendReadReceiptsReceiptTypeCalled: Bool {
return markAsReadSendReadReceiptsReceiptTypeCallsCount > 0
}
var markAsReadSendReadReceiptsReceiptTypeReceivedArguments: (sendReadReceipts: Bool, receiptType: ReceiptType)?
var markAsReadSendReadReceiptsReceiptTypeReceivedInvocations: [(sendReadReceipts: Bool, receiptType: ReceiptType)] = []
var markAsReadSendReadReceiptsReceiptTypeReturnValue: Result<Void, RoomProxyError>!
var markAsReadSendReadReceiptsReceiptTypeClosure: ((Bool, ReceiptType) async -> Result<Void, RoomProxyError>)?
func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
markAsReadSendReadReceiptsReceiptTypeCallsCount += 1
markAsReadSendReadReceiptsReceiptTypeReceivedArguments = (sendReadReceipts: sendReadReceipts, receiptType: receiptType)
markAsReadSendReadReceiptsReceiptTypeReceivedInvocations.append((sendReadReceipts: sendReadReceipts, receiptType: receiptType))
if let markAsReadSendReadReceiptsReceiptTypeClosure = markAsReadSendReadReceiptsReceiptTypeClosure {
return await markAsReadSendReadReceiptsReceiptTypeClosure(sendReadReceipts, receiptType)
} else {
return markAsReadSendReadReceiptsReceiptTypeReturnValue
}
}
//MARK: - canUserJoinCall //MARK: - canUserJoinCall
var canUserJoinCallUserIDCallsCount = 0 var canUserJoinCallUserIDCallsCount = 0
@@ -1,4 +1,4 @@
// Generated using Sourcery 2.1.2 — https://github.com/krzysztofzablocki/Sourcery // Generated using Sourcery 2.1.3 — https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT // DO NOT EDIT
// swiftlint:disable all // swiftlint:disable all
@@ -29,7 +29,6 @@ struct RoomProxyMockConfiguration {
var isEncrypted = true var isEncrypted = true
var hasOngoingCall = true var hasOngoingCall = true
var canonicalAlias: String? var canonicalAlias: String?
var hasUnreadNotifications = Bool.random()
var timeline = { var timeline = {
let mock = TimelineProxyMock() let mock = TimelineProxyMock()
@@ -64,7 +63,6 @@ extension RoomProxyMock {
isEncrypted = configuration.isEncrypted isEncrypted = configuration.isEncrypted
hasOngoingCall = configuration.hasOngoingCall hasOngoingCall = configuration.hasOngoingCall
canonicalAlias = configuration.canonicalAlias canonicalAlias = configuration.canonicalAlias
hasUnreadNotifications = configuration.hasUnreadNotifications
timeline = configuration.timeline timeline = configuration.timeline
@@ -14,13 +14,14 @@
// limitations under the License. // limitations under the License.
// //
import AnalyticsEvents
import SwiftUI import SwiftUI
/// `ScreenTrackerViewModifier` is a helper class used to track PostHog screen from SwiftUI screens. /// `ScreenTrackerViewModifier` is a helper class used to track PostHog screen from SwiftUI screens.
struct ScreenTrackerViewModifier: ViewModifier { struct ScreenTrackerViewModifier: ViewModifier {
@Environment(\.analyticsService) private var analyticsService @Environment(\.analyticsService) private var analyticsService
let screen: AnalyticsScreen let screen: AnalyticsEvent.MobileScreen.ScreenName
@ViewBuilder @ViewBuilder
func body(content: Content) -> some View { func body(content: Content) -> some View {
@@ -32,7 +33,7 @@ struct ScreenTrackerViewModifier: ViewModifier {
} }
extension View { extension View {
func track(screen: AnalyticsScreen) -> some View { func track(screen: AnalyticsEvent.MobileScreen.ScreenName) -> some View {
modifier(ScreenTrackerViewModifier(screen: screen)) modifier(ScreenTrackerViewModifier(screen: screen))
} }
} }
@@ -156,7 +156,7 @@ private extension View {
case .edit: case .edit:
self self
case .new: case .new:
track(screen: .createPoll) track(screen: .CreatePollView)
} }
} }
} }
@@ -33,7 +33,7 @@ struct CreateRoomScreen: View {
securitySection securitySection
} }
.compoundList() .compoundList()
.track(screen: .createRoom) .track(screen: .CreateRoom)
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.navigationTitle(L10n.screenCreateRoomTitle) .navigationTitle(L10n.screenCreateRoomTitle)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
@@ -51,6 +51,8 @@ enum HomeScreenViewAction {
case updateVisibleItemRange(range: Range<Int>, isScrolling: Bool) case updateVisibleItemRange(range: Range<Int>, isScrolling: Bool)
case selectInvites case selectInvites
case globalSearch case globalSearch
case markRoomAsUnread(roomIdentifier: String)
case markRoomAsRead(roomIdentifier: String)
} }
enum HomeScreenRoomListMode: CustomStringConvertible { enum HomeScreenRoomListMode: CustomStringConvertible {
@@ -102,7 +104,9 @@ struct HomeScreenViewState: BindableState {
var rooms: [HomeScreenRoom] = [] var rooms: [HomeScreenRoom] = []
var roomListMode: HomeScreenRoomListMode = .skeletons var roomListMode: HomeScreenRoomListMode = .skeletons
var shouldShowFilters = false var shouldShowFilters = false
var markAsUnreadEnabled = false
var hasPendingInvitations = false var hasPendingInvitations = false
var hasUnreadPendingInvitations = false var hasUnreadPendingInvitations = false
@@ -153,6 +157,8 @@ struct HomeScreenRoom: Identifiable, Equatable {
var name = "" var name = ""
var isMarkedUnread: Bool
var hasUnreadMessages = false var hasUnreadMessages = false
var hasUnreadMentions = false var hasUnreadMentions = false
@@ -171,10 +177,15 @@ struct HomeScreenRoom: Identifiable, Equatable {
var isPlaceholder = false var isPlaceholder = false
var hasNewContent: Bool {
hasUnreadMessages || hasUnreadMentions || hasUnreadNotifications || isMarkedUnread
}
static func placeholder() -> HomeScreenRoom { static func placeholder() -> HomeScreenRoom {
HomeScreenRoom(id: UUID().uuidString, HomeScreenRoom(id: UUID().uuidString,
roomId: nil, roomId: nil,
name: "Placeholder room name", name: "Placeholder room name",
isMarkedUnread: false,
hasUnreadMessages: false, hasUnreadMessages: false,
hasUnreadMentions: false, hasUnreadMentions: false,
hasUnreadNotifications: false, hasUnreadNotifications: false,
@@ -86,6 +86,10 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
.weakAssign(to: \.state.shouldShowFilters, on: self) .weakAssign(to: \.state.shouldShowFilters, on: self)
.store(in: &cancellables) .store(in: &cancellables)
appSettings.$markAsUnreadEnabled
.weakAssign(to: \.state.markAsUnreadEnabled, on: self)
.store(in: &cancellables)
let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused) let isSearchFieldFocused = context.$viewState.map(\.bindings.isSearchFieldFocused)
let searchQuery = context.$viewState.map(\.bindings.searchQuery) let searchQuery = context.$viewState.map(\.bindings.searchQuery)
isSearchFieldFocused isSearchFieldFocused
@@ -143,6 +147,34 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
actionsSubject.send(.presentInvitesScreen) actionsSubject.send(.presentInvitesScreen)
case .globalSearch: case .globalSearch:
actionsSubject.send(.presentGlobalSearch) actionsSubject.send(.presentGlobalSearch)
case .markRoomAsUnread(let roomIdentifier):
Task {
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
MXLog.error("Failed retrieving room for identifier: \(roomIdentifier)")
return
}
switch await roomProxy.markAsUnread() {
case .success:
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
case .failure(let error):
MXLog.error("Failed marking room \(roomIdentifier) as unread with error: \(error)")
}
}
case .markRoomAsRead(let roomIdentifier):
Task {
guard let roomProxy = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
MXLog.error("Failed retrieving room for identifier: \(roomIdentifier)")
return
}
switch await roomProxy.markAsRead(sendReadReceipts: true, receiptType: appSettings.sendReadReceiptsEnabled ? .read : .readPrivate) {
case .success:
ServiceLocator.shared.analytics.trackInteraction(name: .MobileRoomListRoomContextMenuUnreadToggle)
case .failure(let error):
MXLog.error("Failed marking room \(roomIdentifier) as read with error: \(error)")
}
}
} }
} }
@@ -327,6 +359,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
return HomeScreenRoom(id: identifier, return HomeScreenRoom(id: identifier,
roomId: details.id, roomId: details.id,
name: details.name, name: details.name,
isMarkedUnread: details.isMarkedUnread,
hasUnreadMessages: details.unreadMessagesCount > 0, hasUnreadMessages: details.unreadMessagesCount > 0,
hasUnreadMentions: details.unreadMentionsCount > 0, hasUnreadMentions: details.unreadMentionsCount > 0,
hasUnreadNotifications: details.unreadNotificationsCount > 0, hasUnreadNotifications: details.unreadNotificationsCount > 0,
@@ -40,7 +40,7 @@ struct HomeScreen: View {
.navigationTitle(L10n.screenRoomlistMainSpaceTitle) .navigationTitle(L10n.screenRoomlistMainSpaceTitle)
.toolbar { toolbar } .toolbar { toolbar }
.background(Color.compound.bgCanvasDefault.ignoresSafeArea()) .background(Color.compound.bgCanvasDefault.ignoresSafeArea())
.track(screen: .home) .track(screen: .Home)
.introspect(.viewController, on: .supportedVersions) { controller in .introspect(.viewController, on: .supportedVersions) { controller in
Task { Task {
if bloomView == nil { if bloomView == nil {
@@ -133,7 +133,7 @@ struct HomeScreenRoomCell: View {
.foregroundColor(.compound.iconAccentTertiary) .foregroundColor(.compound.iconAccentTertiary)
} }
if hasNewContent { if room.hasNewContent {
Circle() Circle()
.frame(width: 12, height: 12) .frame(width: 12, height: 12)
.foregroundColor(isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary) .foregroundColor(isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
@@ -141,21 +141,15 @@ struct HomeScreenRoomCell: View {
} }
} }
} }
private var hasNewContent: Bool {
room.hasUnreadMessages ||
room.hasUnreadMentions ||
room.hasUnreadNotifications
}
private var isHighlighted: Bool { private var isHighlighted: Bool {
guard !room.isPlaceholder && guard !room.isPlaceholder && room.notificationMode != .mute else {
room.notificationMode != .mute else {
return false return false
} }
return room.hasUnreadNotifications || room.hasUnreadMentions
}
return room.hasUnreadNotifications || room.hasUnreadMentions || room.isMarkedUnread
}
private var mentionIcon: some View { private var mentionIcon: some View {
CompoundIcon(\.mention, size: .custom(15), relativeTo: .compound.bodyMD) CompoundIcon(\.mention, size: .custom(15), relativeTo: .compound.bodyMD)
.accessibilityLabel(L10n.a11yNotificationsMentionsOnly) .accessibilityLabel(L10n.a11yNotificationsMentionsOnly)
@@ -223,6 +217,7 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
return HomeScreenRoom(id: UUID().uuidString, return HomeScreenRoom(id: UUID().uuidString,
roomId: details.id, roomId: details.id,
name: details.name, name: details.name,
isMarkedUnread: details.isMarkedUnread,
hasUnreadMessages: details.unreadMessagesCount > 0, hasUnreadMessages: details.unreadMessagesCount > 0,
hasUnreadMentions: details.unreadMentionsCount > 0, hasUnreadMentions: details.unreadMentionsCount > 0,
hasUnreadNotifications: details.unreadNotificationsCount > 0, hasUnreadNotifications: details.unreadNotificationsCount > 0,
@@ -44,8 +44,25 @@ struct HomeScreenRoomList: View {
.redacted(reason: .placeholder) .redacted(reason: .placeholder)
} else { } else {
let isSelected = context.viewState.selectedRoomID == room.id let isSelected = context.viewState.selectedRoomID == room.id
HomeScreenRoomCell(room: room, context: context, isSelected: isSelected) HomeScreenRoomCell(room: room, context: context, isSelected: isSelected)
.contextMenu { .contextMenu {
if context.viewState.markAsUnreadEnabled {
if room.hasNewContent {
Button {
context.send(viewAction: .markRoomAsRead(roomIdentifier: room.id))
} label: {
Text(L10n.screenRoomlistMarkAsRead)
}
} else {
Button {
context.send(viewAction: .markRoomAsUnread(roomIdentifier: room.id))
} label: {
Text(L10n.screenRoomlistMarkAsUnread)
}
}
}
Button { Button {
context.send(viewAction: .showRoomDetails(roomIdentifier: room.id)) context.send(viewAction: .showRoomDetails(roomIdentifier: room.id))
} label: { } label: {
@@ -24,7 +24,7 @@ struct InvitesScreen: View {
.background(Color.compound.bgCanvasDefault.ignoresSafeArea()) .background(Color.compound.bgCanvasDefault.ignoresSafeArea())
.navigationTitle(L10n.actionInvitesList) .navigationTitle(L10n.actionInvitesList)
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.track(screen: .invites) .track(screen: .Invites)
} }
// MARK: - Private // MARK: - Private
@@ -186,6 +186,7 @@ private extension InvitesScreenRoomDetails {
avatarURL: nil, avatarURL: nil,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -208,6 +209,7 @@ private extension InvitesScreenRoomDetails {
avatarURL: avatarURL, avatarURL: avatarURL,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -34,7 +34,7 @@ struct StaticLocationScreen: View {
} }
mapView mapView
} }
.track(screen: context.viewState.isLocationPickerMode ? .locationSend : .locationView) .track(screen: context.viewState.isLocationPickerMode ? .LocationSend : .LocationView)
.navigationTitle(context.viewState.navigationTitle) .navigationTitle(context.viewState.navigationTitle)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
@@ -37,7 +37,7 @@ struct RoomDetailsEditScreen: View {
.navigationTitle(L10n.screenRoomDetailsEditRoomTitle) .navigationTitle(L10n.screenRoomDetailsEditRoomTitle)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
.track(screen: .roomSettings) .track(screen: .RoomSettings)
} }
// MARK: - Private // MARK: - Private
@@ -61,7 +61,7 @@ struct RoomDetailsScreen: View {
} }
} }
} }
.track(screen: .roomDetails) .track(screen: .RoomDetails)
.interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true) .interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true)
} }
@@ -32,7 +32,7 @@ struct RoomMemberDetailsScreen: View {
.compoundList() .compoundList()
.alert(item: $context.ignoreUserAlert, actions: blockUserAlertActions, message: blockUserAlertMessage) .alert(item: $context.ignoreUserAlert, actions: blockUserAlertActions, message: blockUserAlertMessage)
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.track(screen: .user) .track(screen: .User)
.interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true) .interactiveQuickLook(item: $context.mediaPreviewItem, shouldHideControls: true)
} }
@@ -37,7 +37,7 @@ struct RoomMembersListScreen: View {
inviteButton inviteButton
} }
} }
.track(screen: .roomMembers) .track(screen: .RoomMembers)
} }
// MARK: - Private // MARK: - Private
@@ -33,7 +33,7 @@ struct RoomNotificationSettingsScreen: View {
.compoundList() .compoundList()
.navigationTitle(context.viewState.navigationTitle) .navigationTitle(context.viewState.navigationTitle)
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.track(screen: .roomNotifications) .track(screen: .RoomNotifications)
} }
// MARK: - Private // MARK: - Private
@@ -29,7 +29,7 @@ struct RoomNotificationSettingsUserDefinedScreen: View {
.compoundList() .compoundList()
.navigationTitle(context.viewState.navigationTitle) .navigationTitle(context.viewState.navigationTitle)
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.track(screen: .roomNotifications) .track(screen: .RoomNotifications)
} }
// MARK: - Private // MARK: - Private
@@ -428,7 +428,7 @@ class RoomScreenInteractionHandler {
analyticsService.trackComposer(inThread: false, analyticsService.trackComposer(inThread: false,
isEditing: false, isEditing: false,
isReply: false, isReply: false,
messageType: .voiceMessage, messageType: .VoiceMessage,
startsThread: nil) startsThread: nil)
actionsSubject.send(.composer(action: .setMode(mode: .previewVoiceMessage(state: audioPlayerState, waveform: .url(recordingURL), isUploading: true)))) actionsSubject.send(.composer(action: .setMode(mode: .previewVoiceMessage(state: audioPlayerState, waveform: .url(recordingURL), isUploading: true))))
@@ -70,7 +70,7 @@ struct RoomScreen: View {
.environmentObject(context) .environmentObject(context)
} }
.interactiveQuickLook(item: $context.mediaPreviewItem) .interactiveQuickLook(item: $context.mediaPreviewItem)
.track(screen: .room) .track(screen: .Room)
.onDrop(of: ["public.item", "public.file-url"], isTargeted: $dragOver) { providers -> Bool in .onDrop(of: ["public.item", "public.file-url"], isTargeted: $dragOver) { providers -> Bool in
guard let provider = providers.first, guard let provider = providers.first,
provider.isSupportedForPasteOrDrop else { provider.isSupportedForPasteOrDrop else {
@@ -50,6 +50,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
var userSuggestionsEnabled: Bool { get set } var userSuggestionsEnabled: Bool { get set }
var mentionsBadgeEnabled: Bool { get set } var mentionsBadgeEnabled: Bool { get set }
var roomListFiltersEnabled: Bool { get set } var roomListFiltersEnabled: Bool { get set }
var markAsUnreadEnabled: Bool { get set }
var elementCallBaseURL: URL { get set } var elementCallBaseURL: URL { get set }
} }
@@ -55,6 +55,10 @@ struct DeveloperOptionsScreen: View {
Toggle(isOn: $context.roomListFiltersEnabled) { Toggle(isOn: $context.roomListFiltersEnabled) {
Text("Show filters") Text("Show filters")
} }
Toggle(isOn: $context.markAsUnreadEnabled) {
Text("Mark as unread")
}
} }
Section("Element Call") { Section("Element Call") {
@@ -31,7 +31,7 @@ struct NotificationSettingsEditScreen: View {
.compoundList() .compoundList()
.navigationTitle(context.viewState.strings.navigationTitle) .navigationTitle(context.viewState.strings.navigationTitle)
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.track(screen: .settingsDefaultNotifications) .track(screen: .SettingsDefaultNotifications)
} }
// MARK: - Private // MARK: - Private
@@ -48,7 +48,7 @@ struct NotificationSettingsScreen: View {
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar } .toolbar { toolbar }
.alert(item: $context.alertInfo) .alert(item: $context.alertInfo)
.track(screen: .settingsNotifications) .track(screen: .SettingsNotifications)
} }
// MARK: - Private // MARK: - Private
@@ -29,7 +29,7 @@ struct StartChatScreen: View {
} }
} }
.compoundList() .compoundList()
.track(screen: .startChat) .track(screen: .StartChat)
.scrollDismissesKeyboard(.immediately) .scrollDismissesKeyboard(.immediately)
.navigationTitle(L10n.actionStartChat) .navigationTitle(L10n.actionStartChat)
.navigationBarTitleDisplayMode(.inline) .navigationBarTitleDisplayMode(.inline)
@@ -1,53 +0,0 @@
//
// 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 AnalyticsEvents
enum AnalyticsMessageType {
case location(LocationType)
case poll
case text
case voiceMessage
enum LocationType {
case pin
case user
}
}
extension AnalyticsEvent.Composer.MessageType {
init(_ analyticsMessageType: AnalyticsMessageType) {
switch analyticsMessageType {
case .location(let locationType):
self = .init(locationType)
case .poll:
self = .Poll
case .text:
self = .Text
case .voiceMessage:
self = .VoiceMessage
}
}
private init(_ locationType: AnalyticsMessageType.LocationType) {
switch locationType {
case .pin:
self = .LocationPin
case .user:
self = .LocationUser
}
}
}
@@ -1,159 +0,0 @@
//
// Copyright 2021 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 AnalyticsEvents
import Foundation
enum AnalyticsScreen: Int {
case welcome
case login
case register
case forgotPassword
case sidebar
case home
case favourites
case people
case rooms
case searchRooms
case searchMessages
case searchPeople
case searchFiles
case room
case roomPreview
case roomDetails
case roomMembers
case user
case roomSearch
case roomUploads
case roomSettings
case roomNotifications
case roomDirectory
case switchDirectory
case startChat
case createRoom
case settings
case settingsSecurity
case settingsDefaultNotifications
case settingsMentionsAndKeywords
case settingsNotifications
case deactivateAccount
case inviteFriends
case threadList
case spaceMenu
case spaceMembers
case spaceExploreRooms
case dialpad
case spaceBottomSheet
case invites
case createSpace
case locationSend
case locationView
case createPoll
/// The screen name reported to the AnalyticsEvent.
var screenName: AnalyticsEvent.MobileScreen.ScreenName {
switch self {
case .welcome:
return .Welcome
case .login:
return .Login
case .register:
return .Register
case .forgotPassword:
return .ForgotPassword
case .sidebar:
return .Sidebar
case .home:
return .Home
case .favourites:
return .Favourites
case .people:
return .People
case .rooms:
return .Rooms
case .searchRooms:
return .SearchRooms
case .searchMessages:
return .SearchMessages
case .searchPeople:
return .SearchPeople
case .searchFiles:
return .SearchFiles
case .room:
return .Room
case .roomDetails:
return .RoomDetails
case .roomMembers:
return .RoomMembers
case .user:
return .User
case .roomPreview:
return .RoomPreview
case .roomSearch:
return .RoomSearch
case .roomUploads:
return .RoomUploads
case .roomSettings:
return .RoomSettings
case .roomNotifications:
return .RoomNotifications
case .roomDirectory:
return .RoomDirectory
case .switchDirectory:
return .SwitchDirectory
case .startChat:
return .StartChat
case .createRoom:
return .CreateRoom
case .settings:
return .Settings
case .settingsSecurity:
return .SettingsSecurity
case .settingsDefaultNotifications:
return .SettingsDefaultNotifications
case .settingsMentionsAndKeywords:
return .SettingsMentionsAndKeywords
case .settingsNotifications:
return .SettingsNotifications
case .deactivateAccount:
return .DeactivateAccount
case .inviteFriends:
return .InviteFriends
case .threadList:
return .ThreadList
case .spaceMenu:
return .SpaceMenu
case .spaceMembers:
return .SpaceMembers
case .spaceExploreRooms:
return .SpaceExploreRooms
case .dialpad:
return .Dialpad
case .spaceBottomSheet:
return .SpaceBottomSheet
case .invites:
return .Invites
case .createSpace:
return .CreateSpace
case .locationSend:
return .LocationSend
case .locationView:
return .LocationView
case .createPoll:
return .CreatePollView
}
}
}
@@ -121,11 +121,15 @@ extension AnalyticsService {
/// Track the presentation of a screen /// Track the presentation of a screen
/// - Parameter screen: The screen that was shown /// - Parameter screen: The screen that was shown
/// - Parameter duration: An optional value representing how long the screen was shown for in milliseconds. /// - Parameter duration: An optional value representing how long the screen was shown for in milliseconds.
func track(screen: AnalyticsScreen, duration milliseconds: Int? = nil) { func track(screen: AnalyticsEvent.MobileScreen.ScreenName, duration milliseconds: Int? = nil) {
MXLog.debug("\(screen)") MXLog.debug("\(screen)")
let event = AnalyticsEvent.MobileScreen(durationMs: milliseconds, screenName: screen.screenName) let event = AnalyticsEvent.MobileScreen(durationMs: milliseconds, screenName: screen)
client.screen(event) client.screen(event)
} }
func trackInteraction(index: Int? = nil, name: AnalyticsEvent.Interaction.Name) {
capture(event: AnalyticsEvent.Interaction(index: index, interactionType: .Touch, name: name))
}
/// Track the creation of a room /// Track the creation of a room
/// - Parameter isDM: true if the created room is a direct message, false otherwise /// - Parameter isDM: true if the created room is a direct message, false otherwise
@@ -143,12 +147,12 @@ extension AnalyticsService {
func trackComposer(inThread: Bool, func trackComposer(inThread: Bool,
isEditing: Bool, isEditing: Bool,
isReply: Bool, isReply: Bool,
messageType: AnalyticsMessageType = .text, messageType: AnalyticsEvent.Composer.MessageType = .Text,
startsThread: Bool?) { startsThread: Bool?) {
capture(event: AnalyticsEvent.Composer(inThread: inThread, capture(event: AnalyticsEvent.Composer(inThread: inThread,
isEditing: isEditing, isEditing: isEditing,
isReply: isReply, isReply: isReply,
messageType: .init(messageType), messageType: messageType,
startsThread: startsThread)) startsThread: startsThread))
} }
@@ -587,20 +587,20 @@ class ClientProxy: ClientProxyProtocol {
} }
private let eventFilters: TimelineEventTypeFilter = { private let eventFilters: TimelineEventTypeFilter = {
let stateEventFilters: [FilterStateEventType] = [.roomAliases, let stateEventFilters: [StateEventType] = [.roomAliases,
.roomCanonicalAlias, .roomCanonicalAlias,
.roomGuestAccess, .roomGuestAccess,
.roomHistoryVisibility, .roomHistoryVisibility,
.roomJoinRules, .roomJoinRules,
.roomPinnedEvents, .roomPinnedEvents,
.roomPowerLevels, .roomPowerLevels,
.roomServerAcl, .roomServerAcl,
.roomTombstone, .roomTombstone,
.spaceChild, .spaceChild,
.spaceParent, .spaceParent,
.policyRuleRoom, .policyRuleRoom,
.policyRuleServer, .policyRuleServer,
.policyRuleUser] .policyRuleUser]
return .exclude(eventTypes: stateEventFilters.map { FilterTimelineEventType.state(eventType: $0) }) return .exclude(eventTypes: stateEventFilters.map { FilterTimelineEventType.state(eventType: $0) })
}() }()
+44 -13
View File
@@ -45,18 +45,24 @@ class RoomProxy: RoomProxyProtocol {
var actions: AnyPublisher<RoomProxyAction, Never> { var actions: AnyPublisher<RoomProxyAction, Never> {
actionsSubject.eraseToAnyPublisher() actionsSubject.eraseToAnyPublisher()
} }
var ownUserID: String { var ownUserID: String {
room.ownUserId() room.ownUserId()
} }
init(roomListItem: RoomListItemProtocol, init?(roomListItem: RoomListItemProtocol,
room: RoomProtocol, room: RoomProtocol,
backgroundTaskService: BackgroundTaskServiceProtocol) async { backgroundTaskService: BackgroundTaskServiceProtocol) async {
self.roomListItem = roomListItem self.roomListItem = roomListItem
self.room = room self.room = room
self.backgroundTaskService = backgroundTaskService self.backgroundTaskService = backgroundTaskService
timeline = await TimelineProxy(timeline: room.timeline(), backgroundTaskService: backgroundTaskService)
do {
timeline = try await TimelineProxy(timeline: room.timeline(), backgroundTaskService: backgroundTaskService)
} catch {
MXLog.error("Failed creating timeline with error: \(error)")
return nil
}
Task { Task {
await updateMembers() await updateMembers()
@@ -124,11 +130,7 @@ class RoomProxy: RoomProxyProtocol {
var canonicalAlias: String? { var canonicalAlias: String? {
room.canonicalAlias() room.canonicalAlias()
} }
var hasUnreadNotifications: Bool {
roomListItem.hasUnreadNotifications()
}
var avatarURL: URL? { var avatarURL: URL? {
roomListItem.avatarUrl().flatMap(URL.init(string:)) roomListItem.avatarUrl().flatMap(URL.init(string:))
} }
@@ -352,6 +354,37 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
func markAsUnread() async -> Result<Void, RoomProxyError> {
MXLog.info("Marking room \(id) as unread")
do {
try await room.markAsUnread()
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as unread with error: \(error)")
return .failure(.failedMarkingAsUnread)
}
}
func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result<Void, RoomProxyError> {
MXLog.info("Marking room \(id) as read, sending read receipts: \(sendReadReceipts)")
do {
if sendReadReceipts {
try await room.markAsReadAndSendReadReceipt(receiptType: receiptType)
} else {
try await room.markAsRead()
}
return .success(())
} catch {
MXLog.error("Failed marking room \(id) as read with error: \(error)")
return .failure(.failedMarkingAsRead)
}
}
// MARK: - Element Call
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> { func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> {
do { do {
return try await .success(room.canUserSendState(userId: userID, stateEvent: .callMember)) return try await .success(room.canUserSendState(userId: userID, stateEvent: .callMember))
@@ -361,8 +394,6 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
// MARK: - Element Call
func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol { func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol {
ElementCallWidgetDriver(room: room) ElementCallWidgetDriver(room: room)
} }
@@ -31,6 +31,8 @@ enum RoomProxyError: Error, Equatable {
case failedRemovingAvatar case failedRemovingAvatar
case failedUploadingAvatar case failedUploadingAvatar
case failedCheckingPermission case failedCheckingPermission
case failedMarkingAsRead
case failedMarkingAsUnread
} }
enum RoomProxyAction { enum RoomProxyAction {
@@ -47,7 +49,6 @@ protocol RoomProxyProtocol {
var membership: Membership { get } var membership: Membership { get }
var hasOngoingCall: Bool { get } var hasOngoingCall: Bool { get }
var canonicalAlias: String? { get } var canonicalAlias: String? { get }
var hasUnreadNotifications: Bool { get }
var ownUserID: String { get } var ownUserID: String { get }
var name: String? { get } var name: String? { get }
@@ -103,10 +104,14 @@ protocol RoomProxyProtocol {
func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError> func canUserTriggerRoomNotification(userID: String) async -> Result<Bool, RoomProxyError>
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError> func markAsUnread() async -> Result<Void, RoomProxyError>
func markAsRead(sendReadReceipts: Bool, receiptType: ReceiptType) async -> Result<Void, RoomProxyError>
// MARK: - Element Call // MARK: - Element Call
func canUserJoinCall(userID: String) async -> Result<Bool, RoomProxyError>
func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol func elementCallWidgetDriver() -> ElementCallWidgetDriverProtocol
} }
@@ -83,6 +83,7 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"), lastMessage: AttributedString("I do not wish to take the trouble to understand mysticism"),
lastMessageFormattedTimestamp: "14:56", lastMessageFormattedTimestamp: "14:56",
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -96,6 +97,7 @@ extension Array where Element == RoomSummary {
avatarURL: URL.picturesDirectory, avatarURL: URL.picturesDirectory,
lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"), lastMessage: AttributedString("How do you see the Emperor then? You think he keeps office hours?"),
lastMessageFormattedTimestamp: "2:56 PM", lastMessageFormattedTimestamp: "2:56 PM",
isMarkedUnread: false,
unreadMessagesCount: 2, unreadMessagesCount: 2,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 2, unreadNotificationsCount: 2,
@@ -109,6 +111,7 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"), lastMessage: try? AttributedString(markdown: "He certainly seemed no *mental genius* to me"),
lastMessageFormattedTimestamp: "Some time ago", lastMessageFormattedTimestamp: "Some time ago",
isMarkedUnread: false,
unreadMessagesCount: 3, unreadMessagesCount: 3,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -122,6 +125,7 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("There's an archaic measure of time that's called the month"), lastMessage: AttributedString("There's an archaic measure of time that's called the month"),
lastMessageFormattedTimestamp: "Just now", lastMessageFormattedTimestamp: "Just now",
isMarkedUnread: false,
unreadMessagesCount: 2, unreadMessagesCount: 2,
unreadMentionsCount: 2, unreadMentionsCount: 2,
unreadNotificationsCount: 2, unreadNotificationsCount: 2,
@@ -135,6 +139,7 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"), lastMessage: AttributedString("Clearly, if Earth is powerful enough to do that, it might also be capable of adjusting minds in order to force belief in its radioactivity"),
lastMessageFormattedTimestamp: "1986", lastMessageFormattedTimestamp: "1986",
isMarkedUnread: false,
unreadMessagesCount: 1, unreadMessagesCount: 1,
unreadMentionsCount: 1, unreadMentionsCount: 1,
unreadNotificationsCount: 1, unreadNotificationsCount: 1,
@@ -148,6 +153,7 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString("Are you groping for the word 'paranoia'?"), lastMessage: AttributedString("Are you groping for the word 'paranoia'?"),
lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです", lastMessageFormattedTimestamp: "きょうはじゅういちがつじゅういちにちです",
isMarkedUnread: false,
unreadMessagesCount: 6, unreadMessagesCount: 6,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -161,6 +167,7 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -206,6 +213,7 @@ extension Array where Element == RoomSummary {
avatarURL: URL.picturesDirectory, avatarURL: URL.picturesDirectory,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -219,6 +227,7 @@ extension Array where Element == RoomSummary {
avatarURL: nil, avatarURL: nil,
lastMessage: nil, lastMessage: nil,
lastMessageFormattedTimestamp: nil, lastMessageFormattedTimestamp: nil,
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -24,6 +24,7 @@ struct RoomSummaryDetails {
let avatarURL: URL? let avatarURL: URL?
let lastMessage: AttributedString? let lastMessage: AttributedString?
let lastMessageFormattedTimestamp: String? let lastMessageFormattedTimestamp: String?
let isMarkedUnread: Bool
let unreadMessagesCount: UInt let unreadMessagesCount: UInt
let unreadMentionsCount: UInt let unreadMentionsCount: UInt
let unreadNotificationsCount: UInt let unreadNotificationsCount: UInt
@@ -54,6 +55,7 @@ extension RoomSummaryDetails {
avatarURL = nil avatarURL = nil
lastMessage = AttributedString(string) lastMessage = AttributedString(string)
lastMessageFormattedTimestamp = "Now" lastMessageFormattedTimestamp = "Now"
isMarkedUnread = false
unreadMessagesCount = hasUnreadMessages ? 1 : 0 unreadMessagesCount = hasUnreadMessages ? 1 : 0
unreadMentionsCount = hasUnreadMentions ? 1 : 0 unreadMentionsCount = hasUnreadMentions ? 1 : 0
unreadNotificationsCount = hasUnreadNotifications ? 1 : 0 unreadNotificationsCount = hasUnreadNotifications ? 1 : 0
@@ -248,6 +248,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)), avatarURL: roomInfo.avatarUrl.flatMap(URL.init(string:)),
lastMessage: attributedLastMessage, lastMessage: attributedLastMessage,
lastMessageFormattedTimestamp: lastMessageFormattedTimestamp, lastMessageFormattedTimestamp: lastMessageFormattedTimestamp,
isMarkedUnread: appSettings.markAsUnreadEnabled ? roomInfo.isMarkedUnread : false,
unreadMessagesCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMessages) : 0, unreadMessagesCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMessages) : 0,
unreadMentionsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMentions) : 0, unreadMentionsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadMentions) : 0,
unreadNotificationsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadNotifications) : UInt(roomInfo.notificationCount), unreadNotificationsCount: appSettings.mentionsBadgeEnabled ? UInt(roomInfo.numUnreadNotifications) : UInt(roomInfo.notificationCount),
@@ -92,8 +92,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
} }
func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError> { func sendReadReceipt(for itemID: TimelineItemIdentifier) async -> Result<Void, RoomTimelineControllerError> {
guard roomProxy.hasUnreadNotifications, guard let eventID = itemID.eventID
let eventID = itemID.eventID
else { return .success(()) } else { return .success(()) }
switch await roomProxy.timeline.sendReadReceipt(for: eventID, switch await roomProxy.timeline.sendReadReceipt(for: eventID,
+1
View File
@@ -229,6 +229,7 @@ class LoggingTests: XCTestCase {
avatarURL: nil, avatarURL: nil,
lastMessage: AttributedString(lastMessage), lastMessage: AttributedString(lastMessage),
lastMessageFormattedTimestamp: "Now", lastMessageFormattedTimestamp: "Now",
isMarkedUnread: false,
unreadMessagesCount: 0, unreadMessagesCount: 0,
unreadMentionsCount: 0, unreadMentionsCount: 0,
unreadNotificationsCount: 0, unreadNotificationsCount: 0,
@@ -579,7 +579,6 @@ class RoomScreenViewModelTests: XCTestCase {
timelineProxy.sendReadReceiptForTypeReturnValue = .success(()) timelineProxy.sendReadReceiptForTypeReturnValue = .success(())
roomProxy.underlyingHasUnreadNotifications = true
timelineController.timelineItems = items timelineController.timelineItems = items
timelineController.roomProxy = roomProxy timelineController.roomProxy = roomProxy
+1
View File
@@ -0,0 +1 @@
Add support for manually marking a room as read/unread
+1
View File
@@ -0,0 +1 @@
Remove unnecessary analytics abstraction levels, directly create analytics events in the analytics service
+3 -2
View File
@@ -47,7 +47,7 @@ packages:
# Element/Matrix dependencies # Element/Matrix dependencies
MatrixRustSDK: MatrixRustSDK:
url: https://github.com/matrix-org/matrix-rust-components-swift url: https://github.com/matrix-org/matrix-rust-components-swift
exactVersion: 1.1.36 exactVersion: 1.1.37
# path: ../matrix-rust-sdk # path: ../matrix-rust-sdk
Compound: Compound:
url: https://github.com/element-hq/compound-ios url: https://github.com/element-hq/compound-ios
@@ -55,7 +55,8 @@ packages:
# path: ../compound-ios # path: ../compound-ios
AnalyticsEvents: AnalyticsEvents:
url: https://github.com/matrix-org/matrix-analytics-events url: https://github.com/matrix-org/matrix-analytics-events
minorVersion: 0.9.0 # minorVersion: 0.9.0
revision: 28d1e65d306420b3affc32f719cb516f74a7406f
Emojibase: Emojibase:
url: https://github.com/matrix-org/emojibase-bindings url: https://github.com/matrix-org/emojibase-bindings
minorVersion: 1.0.0 minorVersion: 1.0.0