diff --git a/CHANGES.md b/CHANGES.md index 8a53a1a3c3..dab864dd6a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,23 @@ +## Changes in 0.21.0 (2022-01-25) + +✨ Features + +- MXRoomSummaryStore & MXRoomListDataManager: Implementation with Core Data. ([#4384](https://github.com/vector-im/element-ios/issues/4384)) +- Allow editing poll start events. ([#5114](https://github.com/vector-im/element-ios/issues/5114)) +- Added static location sharing sending and rendering support. ([#5298](https://github.com/vector-im/element-ios/issues/5298)) + +🙌 Improvements + +- MXCoreDataRoomSummaryStore: Use nested contexts to better manage main context updates. ([#5412](https://github.com/vector-im/element-ios/issues/5412)) +- Only count joined rooms when profiling sync performance. ([#5429](https://github.com/vector-im/element-ios/issues/5429)) + +🐛 Bugfixes + +- Fixes DTMF(dial tones) during voice calls. ([#5375](https://github.com/vector-im/element-ios/issues/5375)) +- MXCoreDataRoomListDataFetcher: Update fetchRequest if properties changed before fetching the first page. ([#5377](https://github.com/vector-im/element-ios/issues/5377)) +- MXSession: Fix remove room race case. ([#5412](https://github.com/vector-im/element-ios/issues/5412)) + + ## Changes in 0.20.16 (2022-01-11) 🙌 Improvements diff --git a/MatrixSDK.podspec b/MatrixSDK.podspec index af95c346b9..c7e0d02556 100644 --- a/MatrixSDK.podspec +++ b/MatrixSDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "MatrixSDK" - s.version = "0.20.16" + s.version = "0.21.0" s.summary = "The iOS SDK to build apps compatible with Matrix (https://www.matrix.org)" s.description = <<-DESC @@ -22,17 +22,19 @@ Pod::Spec.new do |s| s.requires_arc = true s.swift_versions = ['5.1', '5.2'] - s.ios.deployment_target = "9.0" - s.osx.deployment_target = "10.10" + s.ios.deployment_target = "10.0" + s.osx.deployment_target = "10.12" s.default_subspec = 'Core' s.subspec 'Core' do |ss| - ss.ios.deployment_target = "9.0" - ss.osx.deployment_target = "10.10" + ss.ios.deployment_target = "10.0" + ss.osx.deployment_target = "10.12" ss.source_files = "MatrixSDK", "MatrixSDK/**/*.{h,m}", "MatrixSDK/**/*.{swift}" ss.osx.exclude_files = "MatrixSDK/VoIP/MXiOSAudioOutputRoute*.swift" ss.private_header_files = ['MatrixSDK/MatrixSDKSwiftHeader.h', "MatrixSDK/**/*_Private.h"] + ss.resources = "MatrixSDK/**/*.{xcdatamodeld}" + ss.frameworks = "CoreData" ss.dependency 'AFNetworking', '~> 4.0.0' ss.dependency 'GZIP', '~> 1.3.0' diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index b74196a28d..162f653aeb 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -729,7 +729,7 @@ B105CDD7261F54C8006EB204 /* MXSpaceChildContent.h in Headers */ = {isa = PBXBuildFile; fileRef = B105CDD4261F54C8006EB204 /* MXSpaceChildContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; B105CDD8261F54C8006EB204 /* MXSpaceChildContent.m in Sources */ = {isa = PBXBuildFile; fileRef = B105CDD5261F54C8006EB204 /* MXSpaceChildContent.m */; }; B105CDD9261F54C8006EB204 /* MXSpaceChildContent.m in Sources */ = {isa = PBXBuildFile; fileRef = B105CDD5261F54C8006EB204 /* MXSpaceChildContent.m */; }; - B10AFB4322A970060092E6AF /* MXEventReplace.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4122A970060092E6AF /* MXEventReplace.h */; }; + B10AFB4322A970060092E6AF /* MXEventReplace.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4122A970060092E6AF /* MXEventReplace.h */; settings = {ATTRIBUTES = (Public, ); }; }; B10AFB4422A970060092E6AF /* MXEventReplace.m in Sources */ = {isa = PBXBuildFile; fileRef = B10AFB4222A970060092E6AF /* MXEventReplace.m */; }; B10AFB4722AA8A8E0092E6AF /* MXEventEditsListener.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4522AA8A8D0092E6AF /* MXEventEditsListener.h */; }; B10AFB4822AA8A8E0092E6AF /* MXEventEditsListener.m in Sources */ = {isa = PBXBuildFile; fileRef = B10AFB4622AA8A8D0092E6AF /* MXEventEditsListener.m */; }; @@ -1085,7 +1085,7 @@ B14EF2F72397E90400758AF0 /* MXRoomEventFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 323F3F9220D3F0C700D26D6A /* MXRoomEventFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2F82397E90400758AF0 /* MXUIKitBackgroundModeHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A9E8221EF4026E0081358A /* MXUIKitBackgroundModeHandler.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2F92397E90400758AF0 /* MXCrypto_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 325D1C251DFECE0D0070B8BF /* MXCrypto_Private.h */; }; - B14EF2FA2397E90400758AF0 /* MXEventReplace.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4122A970060092E6AF /* MXEventReplace.h */; }; + B14EF2FA2397E90400758AF0 /* MXEventReplace.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4122A970060092E6AF /* MXEventReplace.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2FB2397E90400758AF0 /* MXOlmSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 32E402B721C957D2004E87A6 /* MXOlmSession.h */; settings = {ATTRIBUTES = (Public, ); }; }; B14EF2FC2397E90400758AF0 /* MXAggregatedReactionsUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */; }; B14EF2FD2397E90400758AF0 /* MXScanRealmInMemoryProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = B146D4C521A5A44E00D8C2C6 /* MXScanRealmInMemoryProvider.h */; }; @@ -1356,6 +1356,44 @@ EC05478525FF99450047ECD7 /* MXRoomAccountDataUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = EC05478125FF99450047ECD7 /* MXRoomAccountDataUpdater.m */; }; EC054973260123BE0047ECD7 /* MXRoomAccountDataUpdating.h in Headers */ = {isa = PBXBuildFile; fileRef = EC054972260123BE0047ECD7 /* MXRoomAccountDataUpdating.h */; settings = {ATTRIBUTES = (Public, ); }; }; EC054974260123BE0047ECD7 /* MXRoomAccountDataUpdating.h in Headers */ = {isa = PBXBuildFile; fileRef = EC054972260123BE0047ECD7 /* MXRoomAccountDataUpdating.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EC0B940D27184E8A00B4D440 /* MXRoomLastMessageMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940727184E8A00B4D440 /* MXRoomLastMessageMO.swift */; }; + EC0B940E27184E8A00B4D440 /* MXRoomLastMessageMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940727184E8A00B4D440 /* MXRoomLastMessageMO.swift */; }; + EC0B940F27184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940827184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift */; }; + EC0B941027184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940827184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift */; }; + EC0B941127184E8A00B4D440 /* MXRoomSummaryMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940927184E8A00B4D440 /* MXRoomSummaryMO.swift */; }; + EC0B941227184E8A00B4D440 /* MXRoomSummaryMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940927184E8A00B4D440 /* MXRoomSummaryMO.swift */; }; + EC0B941327184E8A00B4D440 /* MXRoomMembersCountMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940A27184E8A00B4D440 /* MXRoomMembersCountMO.swift */; }; + EC0B941427184E8A00B4D440 /* MXRoomMembersCountMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940A27184E8A00B4D440 /* MXRoomMembersCountMO.swift */; }; + EC0B941527184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */; }; + EC0B941627184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */; }; + EC0B9418271855CA00B4D440 /* MXCoreDataRoomSummaryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */; }; + EC0B9419271855CA00B4D440 /* MXCoreDataRoomSummaryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */; }; + EC0B941B2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B941A2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift */; }; + EC0B941C2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B941A2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift */; }; + EC0B941E27186C3500B4D440 /* MXRoomListDataSortable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B941D27186C3500B4D440 /* MXRoomListDataSortable.swift */; }; + EC0B941F27186C3500B4D440 /* MXRoomListDataSortable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B941D27186C3500B4D440 /* MXRoomListDataSortable.swift */; }; + EC0B942127186D4600B4D440 /* MXRoomListDataFilterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B942027186D4600B4D440 /* MXRoomListDataFilterable.swift */; }; + EC0B942227186D4600B4D440 /* MXRoomListDataFilterable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B942027186D4600B4D440 /* MXRoomListDataFilterable.swift */; }; + EC0B94242718E3EE00B4D440 /* MXCoreDataRoomListDataFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B94232718E3EE00B4D440 /* MXCoreDataRoomListDataFetcher.swift */; }; + EC0B94252718E3EF00B4D440 /* MXCoreDataRoomListDataFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B94232718E3EE00B4D440 /* MXCoreDataRoomListDataFetcher.swift */; }; + EC0B94272718E64500B4D440 /* CoreDataContextable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B94262718E64500B4D440 /* CoreDataContextable.swift */; }; + EC0B94282718E64500B4D440 /* CoreDataContextable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B94262718E64500B4D440 /* CoreDataContextable.swift */; }; + EC0B942A2718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B94292718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift */; }; + EC0B942B2718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B94292718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift */; }; + EC0B942F271D95CC00B4D440 /* MXFileRoomSummaryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0B942D271D95CC00B4D440 /* MXFileRoomSummaryStore.h */; }; + EC0B9430271D95CC00B4D440 /* MXFileRoomSummaryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0B942D271D95CC00B4D440 /* MXFileRoomSummaryStore.h */; }; + EC0B9431271D95CC00B4D440 /* MXFileRoomSummaryStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0B942E271D95CC00B4D440 /* MXFileRoomSummaryStore.m */; }; + EC0B9432271D95CC00B4D440 /* MXFileRoomSummaryStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0B942E271D95CC00B4D440 /* MXFileRoomSummaryStore.m */; }; + EC0B9436271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0B9434271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h */; }; + EC0B9437271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0B9434271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h */; }; + EC0B9438271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0B9435271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m */; }; + EC0B9439271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0B9435271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m */; }; + EC0B943D271DB68F00B4D440 /* MXVoidRoomSummaryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0B943B271DB68F00B4D440 /* MXVoidRoomSummaryStore.h */; }; + EC0B943E271DB68F00B4D440 /* MXVoidRoomSummaryStore.h in Headers */ = {isa = PBXBuildFile; fileRef = EC0B943B271DB68F00B4D440 /* MXVoidRoomSummaryStore.h */; }; + EC0B943F271DB68F00B4D440 /* MXVoidRoomSummaryStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0B943C271DB68F00B4D440 /* MXVoidRoomSummaryStore.m */; }; + EC0B9440271DB68F00B4D440 /* MXVoidRoomSummaryStore.m in Sources */ = {isa = PBXBuildFile; fileRef = EC0B943C271DB68F00B4D440 /* MXVoidRoomSummaryStore.m */; }; + EC0B944627206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B944527206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift */; }; + EC0B944727206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC0B944527206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift */; }; EC0C51752559388C00F2CC66 /* MXStopwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECB5D98B2552C9B4000AD89C /* MXStopwatch.swift */; }; EC11658D270F3ABF0089FA56 /* RLMRealm+MatrixSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = EC11658B270F3ABF0089FA56 /* RLMRealm+MatrixSDK.h */; }; EC11658E270F3ABF0089FA56 /* RLMRealm+MatrixSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = EC11658B270F3ABF0089FA56 /* RLMRealm+MatrixSDK.h */; }; @@ -1383,8 +1421,8 @@ EC1165C527107E330089FA56 /* MXRoomListDataFilterOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165AB27107E330089FA56 /* MXRoomListDataFilterOptions.swift */; }; EC1165C627107E330089FA56 /* MXRoomListDataFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165AC27107E330089FA56 /* MXRoomListDataFetcher.swift */; }; EC1165C827107E330089FA56 /* MXRoomListData.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165AD27107E330089FA56 /* MXRoomListData.swift */; }; - EC1165CC27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CA27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift */; }; - EC1165CD27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CA27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift */; }; + EC1165CC27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CA27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift */; }; + EC1165CD27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CA27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift */; }; EC1165CE27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CB27107F3E0089FA56 /* MXRoomListDataManagerTests.swift */; }; EC1165CF27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC1165CB27107F3E0089FA56 /* MXRoomListDataManagerTests.swift */; }; EC1165D0271082410089FA56 /* MXDecrypting.h in Headers */ = {isa = PBXBuildFile; fileRef = 3271877C1DA7CB2F0071C818 /* MXDecrypting.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -2370,6 +2408,25 @@ EC05478025FF99450047ECD7 /* MXRoomAccountDataUpdater.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomAccountDataUpdater.h; sourceTree = ""; }; EC05478125FF99450047ECD7 /* MXRoomAccountDataUpdater.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXRoomAccountDataUpdater.m; sourceTree = ""; }; EC054972260123BE0047ECD7 /* MXRoomAccountDataUpdating.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomAccountDataUpdating.h; sourceTree = ""; }; + EC0B940727184E8A00B4D440 /* MXRoomLastMessageMO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomLastMessageMO.swift; sourceTree = ""; }; + EC0B940827184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXUsersTrustLevelSummaryMO.swift; sourceTree = ""; }; + EC0B940927184E8A00B4D440 /* MXRoomSummaryMO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomSummaryMO.swift; sourceTree = ""; }; + EC0B940A27184E8A00B4D440 /* MXRoomMembersCountMO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomMembersCountMO.swift; sourceTree = ""; }; + EC0B940C27184E8A00B4D440 /* MXRoomSummaryCoreDataStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MXRoomSummaryCoreDataStore.xcdatamodel; sourceTree = ""; }; + EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCoreDataRoomSummaryStore.swift; sourceTree = ""; }; + EC0B941A2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+MatrixSDK.swift"; sourceTree = ""; }; + EC0B941D27186C3500B4D440 /* MXRoomListDataSortable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomListDataSortable.swift; sourceTree = ""; }; + EC0B942027186D4600B4D440 /* MXRoomListDataFilterable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomListDataFilterable.swift; sourceTree = ""; }; + EC0B94232718E3EE00B4D440 /* MXCoreDataRoomListDataFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCoreDataRoomListDataFetcher.swift; sourceTree = ""; }; + EC0B94262718E64500B4D440 /* CoreDataContextable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataContextable.swift; sourceTree = ""; }; + EC0B94292718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXCoreDataRoomListDataManager.swift; sourceTree = ""; }; + EC0B942D271D95CC00B4D440 /* MXFileRoomSummaryStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXFileRoomSummaryStore.h; sourceTree = ""; }; + EC0B942E271D95CC00B4D440 /* MXFileRoomSummaryStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXFileRoomSummaryStore.m; sourceTree = ""; }; + EC0B9434271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXMemoryRoomSummaryStore.h; sourceTree = ""; }; + EC0B9435271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXMemoryRoomSummaryStore.m; sourceTree = ""; }; + EC0B943B271DB68F00B4D440 /* MXVoidRoomSummaryStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXVoidRoomSummaryStore.h; sourceTree = ""; }; + EC0B943C271DB68F00B4D440 /* MXVoidRoomSummaryStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXVoidRoomSummaryStore.m; sourceTree = ""; }; + EC0B944527206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXCoreDataRoomListDataManagerUnitTests.swift; sourceTree = ""; }; EC11658B270F3ABF0089FA56 /* RLMRealm+MatrixSDK.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RLMRealm+MatrixSDK.h"; sourceTree = ""; }; EC11658C270F3ABF0089FA56 /* RLMRealm+MatrixSDK.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RLMRealm+MatrixSDK.m"; sourceTree = ""; }; EC116591270FB6890089FA56 /* MXBackgroundTaskUnitTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXBackgroundTaskUnitTests.swift; sourceTree = ""; }; @@ -2388,7 +2445,7 @@ EC1165AB27107E330089FA56 /* MXRoomListDataFilterOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomListDataFilterOptions.swift; sourceTree = ""; }; EC1165AC27107E330089FA56 /* MXRoomListDataFetcher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomListDataFetcher.swift; sourceTree = ""; }; EC1165AD27107E330089FA56 /* MXRoomListData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomListData.swift; sourceTree = ""; }; - EC1165CA27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomListDataManagerUnitTests.swift; sourceTree = ""; }; + EC1165CA27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXStoreRoomListDataManagerUnitTests.swift; sourceTree = ""; }; EC1165CB27107F3E0089FA56 /* MXRoomListDataManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXRoomListDataManagerTests.swift; sourceTree = ""; }; EC1848C42685F64D00865E16 /* MXiOSAudioOutputRoute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXiOSAudioOutputRoute.swift; sourceTree = ""; }; EC1848C62686174E00865E16 /* MXiOSAudioOutputRouteType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXiOSAudioOutputRouteType.swift; sourceTree = ""; }; @@ -3688,7 +3745,8 @@ ECB6FA8D267CFF4300A941E4 /* MXCredentialsUnitTests.swift */, EC116591270FB6890089FA56 /* MXBackgroundTaskUnitTests.swift */, EC1165CB27107F3E0089FA56 /* MXRoomListDataManagerTests.swift */, - EC1165CA27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift */, + EC1165CA27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift */, + EC0B944527206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift */, 322985CD26FAFC58001890BC /* MatrixSDKTestsSwiftHeader.h */, ); path = MatrixSDKTests; @@ -4133,21 +4191,70 @@ path = JSONModels; sourceTree = ""; }; + EC0B940527184E8A00B4D440 /* CoreData */ = { + isa = PBXGroup; + children = ( + EC0B9417271855C900B4D440 /* MXCoreDataRoomSummaryStore.swift */, + EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */, + EC0B941A2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift */, + EC0B940627184E8A00B4D440 /* Models */, + ); + path = CoreData; + sourceTree = ""; + }; + EC0B940627184E8A00B4D440 /* Models */ = { + isa = PBXGroup; + children = ( + EC0B940727184E8A00B4D440 /* MXRoomLastMessageMO.swift */, + EC0B940827184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift */, + EC0B940927184E8A00B4D440 /* MXRoomSummaryMO.swift */, + EC0B940A27184E8A00B4D440 /* MXRoomMembersCountMO.swift */, + ); + path = Models; + sourceTree = ""; + }; + EC0B942C271D955500B4D440 /* File */ = { + isa = PBXGroup; + children = ( + EC0B942D271D95CC00B4D440 /* MXFileRoomSummaryStore.h */, + EC0B942E271D95CC00B4D440 /* MXFileRoomSummaryStore.m */, + ); + path = File; + sourceTree = ""; + }; + EC0B9433271DB0BB00B4D440 /* Memory */ = { + isa = PBXGroup; + children = ( + EC0B9434271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h */, + EC0B9435271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m */, + ); + path = Memory; + sourceTree = ""; + }; + EC0B943A271DB67900B4D440 /* Void */ = { + isa = PBXGroup; + children = ( + EC0B943B271DB68F00B4D440 /* MXVoidRoomSummaryStore.h */, + EC0B943C271DB68F00B4D440 /* MXVoidRoomSummaryStore.m */, + ); + path = Void; + sourceTree = ""; + }; EC11659C27107E330089FA56 /* RoomList */ = { isa = PBXGroup; children = ( - EC11659D27107E330089FA56 /* MXRoomListDataSortOptions.swift */, EC11659E27107E330089FA56 /* MXRoomListDataManager.swift */, - EC11659F27107E330089FA56 /* MXRoomListDataPaginationOptions.swift */, - EC1165A027107E330089FA56 /* MXStore */, - EC1165A427107E330089FA56 /* CoreData */, - EC1165A527107E330089FA56 /* MXRoomListDataFetcherDelegate.swift */, EC1165A627107E330089FA56 /* MXRoomListDataFetchOptions.swift */, - EC1165A727107E330089FA56 /* Common */, - EC1165AA27107E330089FA56 /* MXRoomListDataCounts.swift */, EC1165AB27107E330089FA56 /* MXRoomListDataFilterOptions.swift */, + EC11659D27107E330089FA56 /* MXRoomListDataSortOptions.swift */, + EC11659F27107E330089FA56 /* MXRoomListDataPaginationOptions.swift */, EC1165AC27107E330089FA56 /* MXRoomListDataFetcher.swift */, + EC1165A527107E330089FA56 /* MXRoomListDataFetcherDelegate.swift */, + EC1165AA27107E330089FA56 /* MXRoomListDataCounts.swift */, EC1165AD27107E330089FA56 /* MXRoomListData.swift */, + EC1165A727107E330089FA56 /* Common */, + EC1165A027107E330089FA56 /* MXStore */, + EC1165A427107E330089FA56 /* CoreData */, ); path = RoomList; sourceTree = ""; @@ -4165,6 +4272,9 @@ EC1165A427107E330089FA56 /* CoreData */ = { isa = PBXGroup; children = ( + EC0B94292718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift */, + EC0B94232718E3EE00B4D440 /* MXCoreDataRoomListDataFetcher.swift */, + EC0B94262718E64500B4D440 /* CoreDataContextable.swift */, ); path = CoreData; sourceTree = ""; @@ -4174,6 +4284,8 @@ children = ( EC1165A827107E330089FA56 /* MXSuggestedRoomListDataFetcher.swift */, EC1165A927107E330089FA56 /* MXSuggestedRoomListDataCache.swift */, + EC0B941D27186C3500B4D440 /* MXRoomListDataSortable.swift */, + EC0B942027186D4600B4D440 /* MXRoomListDataFilterable.swift */, ); path = Common; sourceTree = ""; @@ -4393,6 +4505,10 @@ isa = PBXGroup; children = ( ECD2899D26EB56BC00F268CF /* MXRoomSummaryStore.h */, + EC0B943A271DB67900B4D440 /* Void */, + EC0B9433271DB0BB00B4D440 /* Memory */, + EC0B942C271D955500B4D440 /* File */, + EC0B940527184E8A00B4D440 /* CoreData */, ); path = RoomSummaryStore; sourceTree = ""; @@ -4639,6 +4755,7 @@ 327A5F53239805F600ED6329 /* MXKeyVerificationAccept.h in Headers */, 32A151481DAF7C0C00400192 /* MXKey.h in Headers */, 3A23A741256D322C00B9D00F /* MXAes.h in Headers */, + EC0B943D271DB68F00B4D440 /* MXVoidRoomSummaryStore.h in Headers */, 32BBAE6D2178E99100D85F46 /* MXKeyBackupVersion.h in Headers */, 323547D82226D5D600F15F94 /* MXWellKnownBaseConfig.h in Headers */, 32A151461DAF7C0C00400192 /* MXDeviceInfo.h in Headers */, @@ -4754,10 +4871,12 @@ 327A5F48239805F600ED6329 /* MXKeyVerificationMac.h in Headers */, B1136962230AC9D900E2B2FA /* MXIdentityService.h in Headers */, B19A309C240424BD00FB6F35 /* MXQRCodeTransaction.h in Headers */, + EC0B942F271D95CC00B4D440 /* MXFileRoomSummaryStore.h in Headers */, 9274AFE81EE580240009BEB6 /* MXCallKitAdapter.h in Headers */, EC60EDF2265CFFAC00B39A4E /* MXGroupsSyncResponse.h in Headers */, 321CFDE622525A49004D31DF /* MXSASTransaction.h in Headers */, B19A30CE24042F0800FB6F35 /* MXSelfVerifyingMasterKeyTrustedQRCodeData.h in Headers */, + EC0B9436271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h in Headers */, 32DC15D01A8CF7AE006F9AD3 /* MXNotificationCenter.h in Headers */, 3275FD9C21A6B60B00B9C13D /* MXLoginPolicy.h in Headers */, EC05473425FF8A3C0047ECD7 /* MXVirtualRoomInfo.h in Headers */, @@ -4867,6 +4986,7 @@ B14EF2BD2397E90400758AF0 /* MXAggregationPaginatedResponse.h in Headers */, 3229535225A5F7220012FCF0 /* MXBackgroundCryptoStore.h in Headers */, B14EF2BE2397E90400758AF0 /* MXEvent.h in Headers */, + EC0B9430271D95CC00B4D440 /* MXFileRoomSummaryStore.h in Headers */, B14EF2BF2397E90400758AF0 /* MXRoomThirdPartyInvite.h in Headers */, ECBF657F26DE2A4900AA3A99 /* MXMemoryRoomOutgoingMessagesStore.h in Headers */, EC8A538C25B1BC77004E0802 /* MXCallCandidate.h in Headers */, @@ -5026,6 +5146,7 @@ B14EF3232397E90400758AF0 /* MXRoomState.h in Headers */, 32F00ABC2488E1CD00131741 /* MXRecoveryService.h in Headers */, 32CEEF4A23B0A8170039BA98 /* MXCrossSigningTools.h in Headers */, + EC0B9437271DB0D600B4D440 /* MXMemoryRoomSummaryStore.h in Headers */, B14EF3242397E90400758AF0 /* MXBugReportRestClient.h in Headers */, 323F877C25546170009E9E67 /* MXBaseProfiler.h in Headers */, B14EF3252397E90400758AF0 /* MXRoomPowerLevels.h in Headers */, @@ -5114,6 +5235,7 @@ B14EF35B2397E90400758AF0 /* MXKeyBackupData.h in Headers */, B14EF35C2397E90400758AF0 /* MXPusher.h in Headers */, B14EF35D2397E90400758AF0 /* MXLogger.h in Headers */, + EC0B943E271DB68F00B4D440 /* MXVoidRoomSummaryStore.h in Headers */, B14EF35E2397E90400758AF0 /* MXMegolmBackupCreationInfo.h in Headers */, B14EF35F2397E90400758AF0 /* MXCryptoConstants.h in Headers */, 32AF9285240EA2430008A0FD /* MXSecretShareRequest.h in Headers */, @@ -5447,6 +5569,7 @@ 32792BDD2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m in Sources */, 3259CD541DF860C300186944 /* MXRealmCryptoStore.m in Sources */, EC60EDAA265CFE3B00B39A4E /* MXRoomSyncTimeline.m in Sources */, + EC0B9438271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m in Sources */, EC8A53E225B1BCC6004E0802 /* MXThirdPartyUserInstance.m in Sources */, 3AB5EBB7270ED1C00058703A /* MXSpaceFileStore.swift in Sources */, 321B41401E09937E009EEEC7 /* MXRoomSummary.m in Sources */, @@ -5472,15 +5595,19 @@ 3251D41F25AF01D7001E6E77 /* MXUIKitApplicationStateService.swift in Sources */, B1136965230AC9D900E2B2FA /* MXIdentityService.m in Sources */, B11BD44922CB56790064D8B0 /* MXReplyEventParser.m in Sources */, + EC0B941127184E8A00B4D440 /* MXRoomSummaryMO.swift in Sources */, + EC0B941327184E8A00B4D440 /* MXRoomMembersCountMO.swift in Sources */, 323360701A403A0D0071A488 /* MXFileStore.m in Sources */, B1136967230C1E8600E2B2FA /* MXIdentityService.swift in Sources */, 32A9770521626E5C00919CC0 /* MXServerNotices.m in Sources */, EC1165C827107E330089FA56 /* MXRoomListData.swift in Sources */, 323F878F25553D84009E9E67 /* MXTaskProfile.m in Sources */, 32D7767E1A27860600FC4AA2 /* MXMemoryStore.m in Sources */, + EC0B942127186D4600B4D440 /* MXRoomListDataFilterable.swift in Sources */, 324DD2B8246C21C700377005 /* MXSecretStorageKeyCreationInfo.m in Sources */, EC1165C027107E330089FA56 /* MXSuggestedRoomListDataCache.swift in Sources */, ECD2897726E8ED0900F268CF /* MXRoomListDataFetchOptions.swift in Sources */, + EC0B941B2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift in Sources */, 327E9AE82285A8C400A98BC1 /* MXAggregationPaginatedResponse.m in Sources */, B10AFB4422A970060092E6AF /* MXEventReplace.m in Sources */, EC383BB62540E15E002FBBE6 /* MXBackgroundPushRulesManager.swift in Sources */, @@ -5564,6 +5691,7 @@ 021AFBA52179E91900742B2C /* MXEncryptedContentKey.m in Sources */, 32F634AC1FC5E3480054EF49 /* MXEventDecryptionResult.m in Sources */, 327137281A24D50A00DB6757 /* MXMyUser.m in Sources */, + EC0B94242718E3EE00B4D440 /* MXCoreDataRoomListDataFetcher.swift in Sources */, C6F935881E5B3BE600FC34BF /* MX3PID.swift in Sources */, B146D47821A5950800D8C2C6 /* MXMediaScan.m in Sources */, 3A108AA425810FE5005EEBE9 /* MXRawDataKey.m in Sources */, @@ -5571,6 +5699,7 @@ B146D48021A59E2400D8C2C6 /* MXEventScan.m in Sources */, 329E808D224E2E1B00A48C3A /* MXOutgoingSASTransaction.m in Sources */, 1838927A2702F553003F0C4F /* MXSendReplyEventDefaultStringLocalizer.m in Sources */, + EC0B941E27186C3500B4D440 /* MXRoomListDataSortable.swift in Sources */, 32581DEA23C8C0C900832EAA /* MXUserTrustLevel.m in Sources */, 3271878A1DA7DCE50071C818 /* MXOlmEncryption.m in Sources */, 327187861DA7D0220071C818 /* MXOlmDecryption.m in Sources */, @@ -5589,6 +5718,7 @@ 322AB591260CDBC10017E964 /* MXSyncResponseStoreManager.swift in Sources */, 3250E7CB220C913900736CB5 /* MXCryptoTools.m in Sources */, 322691331E5EF77D00966A6E /* MXDeviceListOperation.m in Sources */, + EC0B940D27184E8A00B4D440 /* MXRoomLastMessageMO.swift in Sources */, ECD2897D26E8F06F00F268CF /* MXStoreRoomListDataFetcher.swift in Sources */, 32D2CC0323422462002BD8CA /* MX3PidAddSession.m in Sources */, 3283F7791EAF30F700C1688C /* MXBugReportRestClient.m in Sources */, @@ -5599,7 +5729,9 @@ EC1848C52685F64D00865E16 /* MXiOSAudioOutputRoute.swift in Sources */, EC60EE08265CFFF400B39A4E /* MXGroupSyncProfile.m in Sources */, B19A30A0240424BD00FB6F35 /* MXQRCodeTransaction.m in Sources */, + EC0B941527184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */, C63E78B01F26588000AC692F /* MXRoomPowerLevels.swift in Sources */, + EC0B943F271DB68F00B4D440 /* MXVoidRoomSummaryStore.m in Sources */, EC383BA5253DE6C9002FBBE6 /* MXSyncResponseStore.swift in Sources */, 32CEEF4523AD2A6C0039BA98 /* MXCrossSigningKey.m in Sources */, 327E9AF02289C61100A98BC1 /* MXAggregations.m in Sources */, @@ -5619,8 +5751,10 @@ 320F7D2C260A8E3B00EF8608 /* MXSyncResponseStoreMetaDataModel.swift in Sources */, 021AFBA72179E91900742B2C /* MXEncryptedContentFile.m in Sources */, 3220094619EFBF30008DE41D /* MXSessionEventListener.m in Sources */, + EC0B940F27184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift in Sources */, 320B393C239FD15E00BE2C06 /* MXKeyVerificationRequest.m in Sources */, EC383BAC254030DF002FBBE6 /* MXBackgroundStore.swift in Sources */, + EC0B942A2718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift in Sources */, 32A31BC920D401FC005916C7 /* MXRoomFilter.m in Sources */, EC11658F270F3ABF0089FA56 /* RLMRealm+MatrixSDK.m in Sources */, 32A151471DAF7C0C00400192 /* MXDeviceInfo.m in Sources */, @@ -5653,6 +5787,7 @@ 32FA10CF1FA1C9F700E54233 /* MXOutgoingRoomKeyRequest.m in Sources */, EC05473625FF8A3C0047ECD7 /* MXVirtualRoomInfo.m in Sources */, ECD289C42701DADB00F268CF /* MXTools.swift in Sources */, + EC0B9431271D95CC00B4D440 /* MXFileRoomSummaryStore.m in Sources */, 32322A4C1E575F65005DD155 /* MXAllowedCertificates.m in Sources */, 18C26C3D273C031900805154 /* PollAggregator.swift in Sources */, F03EF5051DF01596009DF592 /* MXLRUCache.m in Sources */, @@ -5666,6 +5801,7 @@ B14766B923D9D9420091F721 /* MXUsersTrustLevelSummary.m in Sources */, 323547D92226D5D600F15F94 /* MXWellKnownBaseConfig.m in Sources */, 327E37B71A974F75007F026F /* MXLogger.m in Sources */, + EC0B94272718E64500B4D440 /* CoreDataContextable.swift in Sources */, 324BE4691E3FADB1008D99D4 /* MXMegolmExportEncryption.m in Sources */, 32A31BBF20D3F2EC005916C7 /* MXFilterObject.m in Sources */, 320DFDE719DD99B60068622A /* MXHTTPClient.m in Sources */, @@ -5697,6 +5833,7 @@ 320BBF441D6C81550079890E /* MXEventsEnumeratorOnArray.m in Sources */, 32618E7220ED2DF500E1D2EA /* MXFilterJSONModel.m in Sources */, 3259D02526037A7200C365DB /* NSDictionary.swift in Sources */, + EC0B9418271855CA00B4D440 /* MXCoreDataRoomSummaryStore.swift in Sources */, ECF29BE52641953C0053E6D6 /* MXAssertedIdentityModel.m in Sources */, 323F8865212D4E480001C73C /* MXMatrixVersions.m in Sources */, 32133026228BFA800070BA9B /* MXReactionCountChangeListener.m in Sources */, @@ -5799,6 +5936,7 @@ 3281E89E19E299C000976E1A /* MXErrorUnitTests.m in Sources */, EC51019D26C41981007D6D88 /* MXSyncResponseUnitTests.swift in Sources */, 3265CB3B1A151C3800E24B2F /* MXRoomStateTests.m in Sources */, + EC0B944627206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift in Sources */, 32CEEF3D23AD134A0039BA98 /* MXCrossSigningTests.m in Sources */, 326D1EF51BFC79300030947B /* MXPushRuleUnitTests.m in Sources */, EC1165CE27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */, @@ -5851,7 +5989,7 @@ 32C03CB62123076F00D92712 /* DirectRoomTests.m in Sources */, 329FB17C1A0A963700A5E88E /* MXRoomMemberTests.m in Sources */, ECE3DFA8270CF69500FB4C96 /* MockRoomSummary.swift in Sources */, - EC1165CC27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift in Sources */, + EC1165CC27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift in Sources */, 3246BDC51A1A0789000A7D62 /* MXRoomStateDynamicTests.m in Sources */, 32B477832638133C00EA5800 /* MXJSONModelUnitTests.m in Sources */, 321EA11D24893A0400E35B02 /* MXCryptoRecoveryServiceTests.m in Sources */, @@ -5920,6 +6058,7 @@ B14EF1E92397E90400758AF0 /* MXRealmMediaScanMapper.m in Sources */, EC8A53E725B1BCC6004E0802 /* MXThirdPartyProtocol.m in Sources */, B19A30D724042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m in Sources */, + EC0B9439271DB0D600B4D440 /* MXMemoryRoomSummaryStore.m in Sources */, B14EF1EA2397E90400758AF0 /* MXRealmMediaScan.m in Sources */, 3AB5EBB8270ED1C00058703A /* MXSpaceFileStore.swift in Sources */, B14EF1EB2397E90400758AF0 /* MXRoomOperation.m in Sources */, @@ -5945,15 +6084,19 @@ 3259D02426037A7200C365DB /* NSArray.swift in Sources */, 3A108A8125810C96005EEBE9 /* MXKeyData.m in Sources */, 3A59A4A025A7A16F00DDA1FC /* MXOlmOutboundGroupSession.m in Sources */, + EC0B941227184E8A00B4D440 /* MXRoomSummaryMO.swift in Sources */, + EC0B941427184E8A00B4D440 /* MXRoomMembersCountMO.swift in Sources */, B14EF1F92397E90400758AF0 /* MXReactionRelation.m in Sources */, B19A30BB2404268600FB6F35 /* MXQRCodeData.m in Sources */, B14EF1FA2397E90400758AF0 /* MXQueuedEncryption.m in Sources */, 32B0E33C23A2989A0054FF1A /* MXEventReferenceChunk.m in Sources */, B1798D0924091A0100308A8F /* MXBase64Tools.m in Sources */, EC1165C127107E330089FA56 /* MXSuggestedRoomListDataCache.swift in Sources */, + EC0B942227186D4600B4D440 /* MXRoomListDataFilterable.swift in Sources */, 32F00ABE2488E1CD00131741 /* MXRecoveryService.m in Sources */, B14EF1FB2397E90400758AF0 /* MXEnumConstants.swift in Sources */, B14EF1FC2397E90400758AF0 /* MXEventRelations.m in Sources */, + EC0B941C2718654B00B4D440 /* NSManagedObject+MatrixSDK.swift in Sources */, EC8A53E325B1BCC6004E0802 /* MXThirdPartyUserInstance.m in Sources */, EC383BB12540688B002FBBE6 /* MXBackgroundSyncService.swift in Sources */, 322AB587260CD5690017E964 /* MXCachedSyncResponse.m in Sources */, @@ -6037,6 +6180,7 @@ ECF29BD4264194BB0053E6D6 /* MXCallAssertedIdentityEventContent.m in Sources */, EC60EDDD265CFF0600B39A4E /* MXInvitedRoomSync.m in Sources */, B14EF2292397E90400758AF0 /* MXRealmEventScanMapper.m in Sources */, + EC0B94252718E3EF00B4D440 /* MXCoreDataRoomListDataFetcher.swift in Sources */, B14EF22A2397E90400758AF0 /* MXReplyEventFormattedBodyParts.m in Sources */, ECD2899626EB3B4200F268CF /* MXStoreRoomListDataFetcher.swift in Sources */, 324AAC7A2399140D00380A66 /* MXKeyVerificationMac.m in Sources */, @@ -6044,6 +6188,7 @@ EC8A53A825B1BC77004E0802 /* MXCallCapabilitiesModel.m in Sources */, B14EF22C2397E90400758AF0 /* MXAccountData.m in Sources */, 1838927B2702F553003F0C4F /* MXSendReplyEventDefaultStringLocalizer.m in Sources */, + EC0B941F27186C3500B4D440 /* MXRoomListDataSortable.swift in Sources */, EC8A539825B1BC77004E0802 /* MXCallReplacesEventContent.m in Sources */, B18B0E6825FBDC3000E32151 /* MXSpace.swift in Sources */, B14EF22D2397E90400758AF0 /* MXRealmReactionCount.m in Sources */, @@ -6062,6 +6207,7 @@ B14EF2372397E90400758AF0 /* MXKeyVerificationManager.m in Sources */, 8EC5110D256822B400EC4E5B /* MXTaggedEvents.m in Sources */, B14EF2382397E90400758AF0 /* MXEvent.m in Sources */, + EC0B940E27184E8A00B4D440 /* MXRoomLastMessageMO.swift in Sources */, EC8A539225B1BC77004E0802 /* MXCallCandidate.m in Sources */, EC1165B727107E330089FA56 /* MXStoreRoomListDataCounts.swift in Sources */, B14EF2392397E90400758AF0 /* MXMediaManager.m in Sources */, @@ -6072,7 +6218,9 @@ B14EF23D2397E90400758AF0 /* MXCallManager.m in Sources */, B14EF23E2397E90400758AF0 /* MXPeekingRoom.m in Sources */, B14EF23F2397E90400758AF0 /* MXEncryptedContentFile.m in Sources */, + EC0B941627184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld in Sources */, EC8A539625B1BC77004E0802 /* MXUserModel.m in Sources */, + EC0B9440271DB68F00B4D440 /* MXVoidRoomSummaryStore.m in Sources */, B14EF2402397E90400758AF0 /* MXSessionEventListener.m in Sources */, EC8A53A625B1BC77004E0802 /* MXCallInviteEventContent.m in Sources */, ECD2899226EB3B3400F268CF /* MXRoomListDataFetcher.swift in Sources */, @@ -6092,8 +6240,10 @@ B14EF2482397E90400758AF0 /* MXEncryptedAttachments.m in Sources */, EC05478525FF99450047ECD7 /* MXRoomAccountDataUpdater.m in Sources */, ECD289A926EBB0FE00F268CF /* MXRoomListDataFetcherDelegate.swift in Sources */, + EC0B941027184E8A00B4D440 /* MXUsersTrustLevelSummaryMO.swift in Sources */, B14EF24A2397E90400758AF0 /* NSArray+MatrixSDK.m in Sources */, B19A30A92404257700FB6F35 /* MXSASKeyVerificationStart.m in Sources */, + EC0B942B2718F55C00B4D440 /* MXCoreDataRoomListDataManager.swift in Sources */, 324DD2B9246C21C700377005 /* MXSecretStorageKeyCreationInfo.m in Sources */, B14EF24B2397E90400758AF0 /* MXServiceTermsRestClient.m in Sources */, EC1165C527107E330089FA56 /* MXRoomListDataFilterOptions.swift in Sources */, @@ -6126,6 +6276,7 @@ B19A30A72404257700FB6F35 /* MXQRCodeKeyVerificationStart.m in Sources */, B14EF25A2397E90400758AF0 /* MXEnumConstants.m in Sources */, B14EF25B2397E90400758AF0 /* MXSession.m in Sources */, + EC0B9432271D95CC00B4D440 /* MXFileRoomSummaryStore.m in Sources */, ECD289C52701DADB00F268CF /* MXTools.swift in Sources */, 18C26C3E273C032900805154 /* PollAggregator.swift in Sources */, B14EF25C2397E90400758AF0 /* MXRoomTombStoneContent.m in Sources */, @@ -6139,6 +6290,7 @@ 3259CFE726026A6F00C365DB /* MXRestClient+Extensions.swift in Sources */, B105CDD9261F54C8006EB204 /* MXSpaceChildContent.m in Sources */, B14EF2622397E90400758AF0 /* MXAntivirusScanStatusFormatter.m in Sources */, + EC0B94282718E64500B4D440 /* CoreDataContextable.swift in Sources */, 324AAC7C2399140D00380A66 /* MXKeyVerificationStart.m in Sources */, B14EF2632397E90400758AF0 /* MXReactionCountChange.m in Sources */, 324AAC762399140D00380A66 /* MXKeyVerificationCancel.m in Sources */, @@ -6170,6 +6322,7 @@ 324AAC772399140D00380A66 /* MXKeyVerificationDone.m in Sources */, B14EF2702397E90400758AF0 /* MXIdentityServerRestClient.swift in Sources */, B14EF2712397E90400758AF0 /* MXSASTransaction.m in Sources */, + EC0B9419271855CA00B4D440 /* MXCoreDataRoomSummaryStore.swift in Sources */, B14EF2722397E90400758AF0 /* MXDiscoveredClientConfig.m in Sources */, B19A30A1240424BD00FB6F35 /* MXQRCodeTransaction.m in Sources */, B14EF2732397E90400758AF0 /* MXRealmEventScan.m in Sources */, @@ -6272,6 +6425,7 @@ 32EEA8402603CA140041425B /* MXRestClientExtensionsTests.m in Sources */, EC51019E26C41981007D6D88 /* MXSyncResponseUnitTests.swift in Sources */, 32B477AA2638186000EA5800 /* MXHTTPAdditionalHeadersUnitTests.m in Sources */, + EC0B944727206D0B00B4D440 /* MXCoreDataRoomListDataManagerUnitTests.swift in Sources */, B1E09A2B2397FD6B0057C069 /* MatrixSDKTestsE2EData.m in Sources */, B1E09A3C2397FD820057C069 /* MXStoreMemoryStoreTests.m in Sources */, EC1165CF27107F3E0089FA56 /* MXRoomListDataManagerTests.swift in Sources */, @@ -6324,7 +6478,7 @@ B1E09A2F2397FD750057C069 /* MXErrorUnitTests.m in Sources */, B1660F1D260A20B900C3AA12 /* MXSpaceServiceTest.swift in Sources */, ECE3DFA9270CF69500FB4C96 /* MockRoomSummary.swift in Sources */, - EC1165CD27107F3E0089FA56 /* MXRoomListDataManagerUnitTests.swift in Sources */, + EC1165CD27107F3E0089FA56 /* MXStoreRoomListDataManagerUnitTests.swift in Sources */, B1E09A2A2397FD680057C069 /* MatrixSDKTestsData.m in Sources */, B1E09A302397FD750057C069 /* MXHTTPClientTests.m in Sources */, EC383BC02542F1E4002FBBE6 /* MXBackgroundSyncServiceTests.swift in Sources */, @@ -6484,6 +6638,7 @@ HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = MatrixSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -6510,6 +6665,7 @@ HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = MatrixSDK/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -6537,6 +6693,7 @@ ); HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = MatrixSDKTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -6562,6 +6719,7 @@ GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = MatrixSDKTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -6587,8 +6745,10 @@ HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/MatrixSDK/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; + MACOSX_DEPLOYMENT_TARGET = 10.12; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixSDK; PRODUCT_NAME = MatrixSDK; @@ -6614,8 +6774,10 @@ HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = "$(SRCROOT)/MatrixSDK/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; + MACOSX_DEPLOYMENT_TARGET = 10.12; OTHER_LDFLAGS = "$(inherited)"; PRODUCT_BUNDLE_IDENTIFIER = org.matrix.MatrixSDK; PRODUCT_NAME = MatrixSDK; @@ -6644,8 +6806,10 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = MatrixSDKTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; + MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "matrix.org.MatrixSDKTests-macOS"; @@ -6675,8 +6839,10 @@ GCC_C_LANGUAGE_STANDARD = gnu11; HEADER_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = MatrixSDKTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; LIBRARY_SEARCH_PATHS = "$(inherited)"; + MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "matrix.org.MatrixSDKTests-macOS"; PRODUCT_NAME = MatrixSDKTests; @@ -6734,6 +6900,19 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + EC0B940B27184E8A00B4D440 /* MXCoreDataRoomSummaryStore.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + EC0B940C27184E8A00B4D440 /* MXRoomSummaryCoreDataStore.xcdatamodel */, + ); + currentVersion = EC0B940C27184E8A00B4D440 /* MXRoomSummaryCoreDataStore.xcdatamodel */; + path = MXCoreDataRoomSummaryStore.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ }; rootObject = 32C6F92419DD814400EA4E9C /* Project object */; } diff --git a/MatrixSDK/Aggregations/MXAggregations.m b/MatrixSDK/Aggregations/MXAggregations.m index 15600ad635..e65b127e50 100644 --- a/MatrixSDK/Aggregations/MXAggregations.m +++ b/MatrixSDK/Aggregations/MXAggregations.m @@ -254,6 +254,7 @@ - (void)registerListener [self.mxSession listenToEvents:^(MXEvent *event, MXTimelineDirection direction, id customObject) { switch (event.eventType) { + case MXEventTypePollStart: case MXEventTypeRoomMessage: if (direction == MXTimelineDirectionForwards && [event.relatesTo.relationType isEqualToString:MXEventRelationTypeReplace]) diff --git a/MatrixSDK/Background/MXBackgroundPushRulesManager.swift b/MatrixSDK/Background/MXBackgroundPushRulesManager.swift index 0d30dd0664..9ee89193f8 100644 --- a/MatrixSDK/Background/MXBackgroundPushRulesManager.swift +++ b/MatrixSDK/Background/MXBackgroundPushRulesManager.swift @@ -51,7 +51,7 @@ import Foundation private var permissionConditionChecker: MXPushRuleSenderNotificationPermissionConditionChecker /// Initializer. - /// - Parameter restClient: Rest client to fetch initial push rules. + /// - Parameter credentials: Credentials to use when fetching initial push rules. public init(withCredentials credentials: MXCredentials) { self.credentials = credentials eventMatchConditionChecker = MXPushRuleEventMatchConditionChecker() diff --git a/MatrixSDK/Background/MXBackgroundStore.swift b/MatrixSDK/Background/MXBackgroundStore.swift index ed3f59e965..ee0ae32ac0 100644 --- a/MatrixSDK/Background/MXBackgroundStore.swift +++ b/MatrixSDK/Background/MXBackgroundStore.swift @@ -259,19 +259,45 @@ class MXBackgroundStore: NSObject, MXStore { return [] } - // MARK: - MXRoomSummaryStore + var roomSummaryStore: MXRoomSummaryStore { + return self + } + +} + +// MARK: - MXRoomSummaryStore + +extension MXBackgroundStore: MXRoomSummaryStore { var rooms: [String] { return [] } - func storeSummary(forRoom roomId: String, summary: MXRoomSummaryProtocol) { + var countOfRooms: UInt { + return 0 + } + + func storeSummary(_ summary: MXRoomSummaryProtocol) { } // Fetch real soom summary func summary(ofRoom roomId: String) -> MXRoomSummaryProtocol? { - return fileStore.summary(ofRoom: roomId) + return fileStore.roomSummaryStore.summary(ofRoom: roomId) + } + + func removeSummary(ofRoom roomId: String) { + + } + + func removeAllSummaries() { + + } + + func fetchAllSummaries(_ completion: @escaping ([MXRoomSummaryProtocol]) -> Void) { + DispatchQueue.main.async { + completion([]) + } } } diff --git a/MatrixSDK/Background/MXBackgroundSyncService.swift b/MatrixSDK/Background/MXBackgroundSyncService.swift index af902d4f61..c7e40f0e9f 100644 --- a/MatrixSDK/Background/MXBackgroundSyncService.swift +++ b/MatrixSDK/Background/MXBackgroundSyncService.swift @@ -179,7 +179,7 @@ public enum MXBackgroundSyncServiceError: Error { /// - Parameter roomId: The room identifier to fetch. /// - Returns: Summary of room. public func roomSummary(forRoomId roomId: String) -> MXRoomSummaryProtocol? { - let summary = store.summary(ofRoom: roomId) + let summary = store.roomSummaryStore.summary(ofRoom: roomId) return syncResponseStoreManager.roomSummary(forRoomId: roomId, using: summary) } diff --git a/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.h b/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.h index 32482751be..cd0b3b8506 100644 --- a/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.h +++ b/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.h @@ -16,6 +16,8 @@ #import +@class MXUsersTrustLevelSummaryMO; + NS_ASSUME_NONNULL_BEGIN /** @@ -31,6 +33,10 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithTrustedUsersProgress:(NSProgress*)trustedUsersProgress andTrustedDevicesProgress:(NSProgress*)trustedDevicesProgress; +#pragma mark - CoreData Model + +- (instancetype)initWithManagedObject:(MXUsersTrustLevelSummaryMO *)model; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.m b/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.m index f820c2091b..f16810dd6c 100644 --- a/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.m +++ b/MatrixSDK/Crypto/Data/Trust/MXUsersTrustLevelSummary.m @@ -15,6 +15,7 @@ */ #import "MXUsersTrustLevelSummary.h" +#import "MatrixSDKSwiftHeader.h" @interface MXUsersTrustLevelSummary() @@ -36,6 +37,20 @@ - (instancetype)initWithTrustedUsersProgress:(NSProgress*)trustedUsersProgress a return self; } +#pragma mark - CoreData Model + +- (instancetype)initWithManagedObject:(MXUsersTrustLevelSummaryMO *)model +{ + if (self = [super init]) + { + self.trustedUsersProgress = [NSProgress progressWithTotalUnitCount:model.s_usersCount]; + self.trustedUsersProgress.completedUnitCount = model.s_trustedUsersCount; + + self.trustedDevicesProgress = [NSProgress progressWithTotalUnitCount:model.s_devicesCount]; + self.trustedDevicesProgress.completedUnitCount = model.s_trustedDevicesCount; + } + return self; +} #pragma mark - NSCoding diff --git a/MatrixSDK/Data/MXRoom.h b/MatrixSDK/Data/MXRoom.h index e26f375b44..c9fede2ef5 100644 --- a/MatrixSDK/Data/MXRoom.h +++ b/MatrixSDK/Data/MXRoom.h @@ -950,6 +950,13 @@ FOUNDATION_EXPORT NSInteger const kMXRoomAlreadyJoinedErrorCode; success:(void (^)(NSString *eventId))success failure:(void (^)(NSError *error))failure; +- (MXHTTPOperation *)sendPollUpdateForEvent:(MXEvent *)pollStartEvent + oldContent:(MXEventContentPollStart *)oldContent + newContent:(MXEventContentPollStart *)newContent + localEcho:(MXEvent **)localEcho + success:(void (^)(NSString *))success + failure:(void (^)(NSError *))failure; + #pragma mark - Location sharing - (MXHTTPOperation *)sendLocationWithLatitude:(double)latitude diff --git a/MatrixSDK/Data/MXRoom.m b/MatrixSDK/Data/MXRoom.m index 944fbbf11a..3d7f4bc784 100644 --- a/MatrixSDK/Data/MXRoom.m +++ b/MatrixSDK/Data/MXRoom.m @@ -147,13 +147,6 @@ + (id)loadRoomFromStore:(id)store withRoomId:(NSString *)roomId matrixS // Report the provided accountData. // Allocate a new instance if none, in order to handle room tag events for this room. room->_accountData = accountData ? accountData : [[MXRoomAccountData alloc] init]; - - // Check whether the room is pending on an invitation. - if (room.summary.membership == MXMembershipInvite) - { - // Handle direct flag to decide if it is direct or not - [room handleInviteDirectFlag]; - } } return room; } @@ -1464,7 +1457,7 @@ - (MXHTTPOperation*)sendVoiceMessage:(NSURL*)fileLocalURL return [self _sendFile:fileLocalURL msgType:kMXMessageTypeAudio additionalTypes:@{kMXMessageContentKeyVoiceMessageMSC3245 : @{}, - kMXMessageContentKeyExtensibleAudio: extensibleAudioContent} + kMXMessageContentKeyExtensibleAudioMSC1767: extensibleAudioContent} mimeType:(mimeType ?: @"audio/ogg") localEcho:localEcho success:success @@ -1541,8 +1534,8 @@ - (MXHTTPOperation*)_sendFile:(NSURL*)fileLocalURL @"mimetype": mimeType, @"size": @(fileData.length) }, - kMXMessageContentKeyExtensibleText: filename, - kMXMessageContentKeyExtensibleFile: @{ + kMXMessageContentKeyExtensibleTextMSC1767: filename, + kMXMessageContentKeyExtensibleFileMSC1767: @{ kMXMessageContentKeyExtensibleFileSize: @(fileData.length), kMXMessageContentKeyExtensibleFileName: filename, kMXMessageContentKeyExtensibleFileURL: fakeMediaURI, @@ -1652,7 +1645,7 @@ - (MXHTTPOperation*)_sendFile:(NSURL*)fileLocalURL } msgContent[@"url"] = nil; - msgContent[kMXMessageContentKeyExtensibleFile][kMXMessageContentKeyExtensibleFileURL] = nil; + msgContent[kMXMessageContentKeyExtensibleFileMSC1767][kMXMessageContentKeyExtensibleFileURL] = nil; msgContent[@"file"] = result.JSONDictionary; MXHTTPOperation *operation2 = [self sendMessageWithContent:msgContent localEcho:&event success:onSuccess failure:onFailure]; @@ -1683,7 +1676,7 @@ - (MXHTTPOperation*)_sendFile:(NSURL*)fileLocalURL // Update the message content with the mxc:// of the media on the homeserver msgContent[@"url"] = url; - msgContent[kMXMessageContentKeyExtensibleFile][kMXMessageContentKeyExtensibleFileURL] = url; + msgContent[kMXMessageContentKeyExtensibleFileMSC1767][kMXMessageContentKeyExtensibleFileURL] = url; // Make the final request that posts the image event MXHTTPOperation *operation2 = [self sendMessageWithContent:msgContent localEcho:&event success:onSuccess failure:onFailure]; @@ -2364,7 +2357,7 @@ - (MXHTTPOperation *)sendPollResponseForEvent:(MXEvent *)pollStartEvent NSDictionary *content = @{ kMXEventRelationRelatesToKey: relatesTo.JSONDictionary, - kMXMessageContentKeyExtensiblePollResponse: @{ kMXMessageContentKeyExtensiblePollAnswers: answerIdentifiers } + kMXMessageContentKeyExtensiblePollResponseMSC3381: @{ kMXMessageContentKeyExtensiblePollAnswers: answerIdentifiers } }; return [self sendEventOfType:[MXTools eventTypeString:MXEventTypePollResponse] content:content localEcho:localEcho success:success failure:failure]; @@ -2383,12 +2376,36 @@ - (MXHTTPOperation *)sendPollEndForEvent:(MXEvent *)pollStartEvent NSDictionary *content = @{ kMXEventRelationRelatesToKey: relatesTo.JSONDictionary, - kMXMessageContentKeyExtensiblePollEnd: @{} + kMXMessageContentKeyExtensiblePollEndMSC3381: @{} }; return [self sendEventOfType:[MXTools eventTypeString:MXEventTypePollEnd] content:content localEcho:localEcho success:success failure:failure]; } +- (MXHTTPOperation *)sendPollUpdateForEvent:(MXEvent *)pollStartEvent + oldContent:(MXEventContentPollStart *)oldContent + newContent:(MXEventContentPollStart *)newContent + localEcho:(MXEvent **)localEcho + success:(void (^)(NSString *))success + failure:(void (^)(NSError *))failure +{ + NSParameterAssert(oldContent); + NSParameterAssert(newContent); + + NSMutableDictionary *content = [NSMutableDictionary dictionary]; + + [content addEntriesFromDictionary:oldContent.JSONDictionary]; + + MXEventContentRelatesTo *relatesTo = [[MXEventContentRelatesTo alloc] initWithRelationType:MXEventRelationTypeReplace + eventId:pollStartEvent.eventId]; + + [content setObject:relatesTo.JSONDictionary forKey:kMXEventRelationRelatesToKey]; + + [content setObject:newContent.JSONDictionary forKey:kMXMessageContentKeyNewContent]; + + return [self sendEventOfType:[MXTools eventTypeString:MXEventTypePollStart] content:content localEcho:localEcho success:success failure:failure]; +} + #pragma mark - Location sharing - (MXHTTPOperation *)sendLocationWithLatitude:(double)latitude @@ -2398,23 +2415,21 @@ - (MXHTTPOperation *)sendLocationWithLatitude:(double)latitude success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure { - NSMutableDictionary *content = [NSMutableDictionary dictionary]; - content[kMXMessageTypeKey] = kMXMessageTypeLocation; - - MXEventContentLocation *locationContent = [[MXEventContentLocation alloc] initWithLatitude:latitude - longitude:longitude - description:description]; - - content[kMXMessageContentKeyExtensibleLocationMSC3488] = locationContent.JSONDictionary; + MXEventContentLocation *locationContent = [[MXEventContentLocation alloc] initWithAssetType:MXEventAssetTypeUser + latitude:latitude + longitude:longitude + description:description]; - content[kMXMessageGeoURIKey] = locationContent.geoURI; + NSMutableDictionary *content = [NSMutableDictionary dictionary]; + + [content addEntriesFromDictionary:locationContent.JSONDictionary]; NSString *fallbackText = [NSString stringWithFormat:@"%@ was at %@ as of %@", self.mxSession.myUser.displayname, locationContent.geoURI, NSDate.date]; content[kMXMessageBodyKey] = fallbackText; - content[kMXMessageContentKeyExtensibleText] = fallbackText; + content[kMXMessageContentKeyExtensibleTextMSC1767] = fallbackText; NSInteger timestamp = NSDate.date.timeIntervalSince1970 * 1000; // milliseconds since UNIX epoch - content[kMXMessageContentKeyExtensibleTimestamp] = @(timestamp); + content[kMXMessageContentKeyExtensibleTimestampMSC3488] = @(timestamp); return [self sendMessageWithContent:content localEcho:localEcho diff --git a/MatrixSDK/Data/MXRoomLastMessage.h b/MatrixSDK/Data/MXRoomLastMessage.h index 9e1278705f..e3c76e9081 100644 --- a/MatrixSDK/Data/MXRoomLastMessage.h +++ b/MatrixSDK/Data/MXRoomLastMessage.h @@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN FOUNDATION_EXPORT NSString *const MXRoomLastMessageDataType; @class MXEvent; +@class MXRoomLastMessageMO; /** `MXRoomLastMessage` is a model class to store some lastMessage properties for room summary objects. @@ -68,6 +69,10 @@ FOUNDATION_EXPORT NSString *const MXRoomLastMessageDataType; - (instancetype)initWithEvent:(MXEvent *)event; +#pragma mark - CoreData Model + +- (instancetype)initWithManagedObject:(MXRoomLastMessageMO *)model; + - (NSComparisonResult)compareOriginServerTs:(MXRoomLastMessage *)otherMessage; @end diff --git a/MatrixSDK/Data/MXRoomLastMessage.m b/MatrixSDK/Data/MXRoomLastMessage.m index 2e7a2dc85b..dbec49468f 100644 --- a/MatrixSDK/Data/MXRoomLastMessage.m +++ b/MatrixSDK/Data/MXRoomLastMessage.m @@ -19,6 +19,7 @@ #import "MXKeyProvider.h" #import "MXAesKeyData.h" #import "MXAes.h" +#import "MatrixSDKSwiftHeader.h" #import #import @@ -76,6 +77,29 @@ - (NSString*)description return [NSString stringWithFormat:@"%@: %@ - %llu", super.description, self.eventId, self.originServerTs]; } +#pragma mark - CoreData Model + +- (instancetype)initWithManagedObject:(MXRoomLastMessageMO *)model +{ + if (self = [super init]) + { + _eventId = model.s_eventId; + _originServerTs = model.s_originServerTs; + _isEncrypted = model.s_isEncrypted; + _sender = model.s_sender; + _text = model.s_text; + if (model.s_attributedText) + { + _attributedText = [NSKeyedUnarchiver unarchiveObjectWithData:model.s_attributedText]; + } + if (model.s_others) + { + _others = [NSKeyedUnarchiver unarchiveObjectWithData:model.s_others]; + } + } + return self; +} + #pragma mark - NSCoding - (instancetype)initWithCoder:(NSCoder *)coder diff --git a/MatrixSDK/Data/MXRoomMembersCount.h b/MatrixSDK/Data/MXRoomMembersCount.h index aeb696c088..d6657c1f39 100644 --- a/MatrixSDK/Data/MXRoomMembersCount.h +++ b/MatrixSDK/Data/MXRoomMembersCount.h @@ -16,6 +16,8 @@ #import +@class MXRoomMembersCountMO; + /** Room members counts. */ @@ -25,6 +27,10 @@ @property (nonatomic) NSUInteger joined; @property (nonatomic) NSUInteger invited; +#pragma mark - CoreData Model + +- (instancetype) initWithManagedObject:(MXRoomMembersCountMO *)model; + /** Initializer with numbers. diff --git a/MatrixSDK/Data/MXRoomMembersCount.m b/MatrixSDK/Data/MXRoomMembersCount.m index 54f3e78950..a446efc74f 100644 --- a/MatrixSDK/Data/MXRoomMembersCount.m +++ b/MatrixSDK/Data/MXRoomMembersCount.m @@ -15,7 +15,7 @@ */ #import "MXRoomMembersCount.h" - +#import "MatrixSDKSwiftHeader.h" @implementation MXRoomMembersCount - (instancetype)initWithMembers:(NSUInteger)members @@ -59,6 +59,14 @@ - (id)copyWithZone:(NSZone *)zone return roomMembersCount; } +#pragma mark - CoreData Model + +- (instancetype)initWithManagedObject:(MXRoomMembersCountMO *)model +{ + return [self initWithMembers:model.s_members + joined:model.s_joined + invited:model.s_invited]; +} #pragma mark - NSCoding - (instancetype)initWithCoder:(NSCoder *)aDecoder diff --git a/MatrixSDK/Data/MXRoomSummary.m b/MatrixSDK/Data/MXRoomSummary.m index 2ae40308aa..35849e26c2 100644 --- a/MatrixSDK/Data/MXRoomSummary.m +++ b/MatrixSDK/Data/MXRoomSummary.m @@ -72,6 +72,10 @@ @interface MXRoomSummary () @implementation MXRoomSummary +@synthesize hasAnyUnread = _hasAnyUnread; +@synthesize hasAnyNotification = _hasAnyNotification; +@synthesize hasAnyHighlight = _hasAnyHighlight; + - (instancetype)init { self = [super init]; @@ -99,6 +103,7 @@ - (instancetype)initWithRoomId:(NSString *)roomId matrixSession:(MXSession *)mxS store = theStore; [self setMatrixSession:mxSession]; + [self commonInit]; } return self; @@ -110,6 +115,7 @@ - (instancetype)initWithSummaryModel:(id)model { _roomId = model.roomId; [self updateWith:model]; + [self commonInit]; } return self; } @@ -120,10 +126,32 @@ - (instancetype)initWithSpaceChildInfo:(MXSpaceChildInfo *)spaceChildInfo { _roomId = spaceChildInfo.childRoomId; _spaceChildInfo = spaceChildInfo; + [self commonInit]; } return self; } +- (void)commonInit +{ + // Listen to the event sent state changes + // This is used to follow evolution of local echo events + // (ex: when a sentState change from sending to sentFailed) + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeSentState:) name:kMXEventDidChangeSentStateNotification object:nil]; + + // Listen to the event id change + // This is used to follow evolution of local echo events + // when they changed their local event id to the final event id + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeIdentifier:) name:kMXEventDidChangeIdentifierNotification object:nil]; + + // Listen to data being flush in a room + // This is used to update the room summary in case of a state event redaction + // We may need to update the room displayname when it happens + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(roomDidFlushData:) name:kMXRoomDidFlushDataNotification object:nil]; + + // Listen to event edits within the room + [self registerEventEditsListener]; +} + - (void)destroy { [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeSentStateNotification object:nil]; @@ -138,25 +166,6 @@ - (void)setMatrixSession:(MXSession *)mxSession { _mxSession = mxSession; store = mxSession.store; - [self updateWith:[store summaryOfRoom:_roomId]]; - - // Listen to the event sent state changes - // This is used to follow evolution of local echo events - // (ex: when a sentState change from sending to sentFailed) - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeSentState:) name:kMXEventDidChangeSentStateNotification object:nil]; - - // Listen to the event id change - // This is used to follow evolution of local echo events - // when they changed their local event id to the final event id - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeIdentifier:) name:kMXEventDidChangeIdentifierNotification object:nil]; - - // Listen to data being flush in a room - // This is used to update the room summary in case of a state event redaction - // We may need to update the room displayname when it happens - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(roomDidFlushData:) name:kMXRoomDidFlushDataNotification object:nil]; - - // Listen to event edits within the room - [self registerEventEditsListener]; } } @@ -165,8 +174,9 @@ - (void)save:(BOOL)commit _dataTypes = self.calculateDataTypes; _sentStatus = self.calculateSentStatus; _favoriteTagOrder = self.room.accountData.tags[kMXRoomTagFavourite].order; + _storedHash = self.hash; - [store storeSummaryForRoom:_roomId summary:self]; + [store.roomSummaryStore storeSummary:self]; if (commit && [store respondsToSelector:@selector(commit)]) { @@ -232,6 +242,9 @@ - (void)updateWith:(id)summary _localUnreadEventCount = summary.localUnreadEventCount; _notificationCount = summary.notificationCount; _highlightCount = summary.highlightCount; + _hasAnyUnread = summary.hasAnyUnread; + _hasAnyNotification = summary.hasAnyNotification; + _hasAnyHighlight = summary.hasAnyHighlight; _directUserId = summary.directUserId; _others = [summary.others mutableCopy]; _favoriteTagOrder = summary.favoriteTagOrder; @@ -1016,11 +1029,19 @@ - (NSUInteger)hash result = prime * result + _localUnreadEventCount; result = prime * result + _notificationCount; result = prime * result + _highlightCount; + result = prime * result + @(_hasAnyUnread).unsignedIntegerValue; + result = prime * result + @(_hasAnyNotification).unsignedIntegerValue; + result = prime * result + @(_hasAnyHighlight).unsignedIntegerValue; result = prime * result + _dataTypes; result = prime * result + _sentStatus; result = prime * result + [_lastMessage.eventId hash]; result = prime * result + [_lastMessage.text hash]; + result = [NSNumber numberWithUnsignedInteger:result].hash; + while (result > INT64_MAX) + { + result -= INT64_MAX; + } return result; } diff --git a/MatrixSDK/Data/MXRoomSummaryProtocol.h b/MatrixSDK/Data/MXRoomSummaryProtocol.h index 8cd46d729b..b3666febd3 100644 --- a/MatrixSDK/Data/MXRoomSummaryProtocol.h +++ b/MatrixSDK/Data/MXRoomSummaryProtocol.h @@ -22,6 +22,8 @@ #import "MXUsersTrustLevelSummary.h" #import "MXRoomSummaryDataTypes.h" #import "MXRoomSummarySentStatus.h" +#import "MXRoomType.h" +#import "MXRoomLastMessage.h" @class MXSession; @class MXSpaceChildInfo; diff --git a/MatrixSDK/Data/MXUser.m b/MatrixSDK/Data/MXUser.m index 981b842fd3..f4b7dcb029 100644 --- a/MatrixSDK/Data/MXUser.m +++ b/MatrixSDK/Data/MXUser.m @@ -151,7 +151,7 @@ - (void)updateFromHomeserverOfMatrixSession:(MXSession *)mxSession success:(void self.displayname = displayname; self.avatarUrl = avatarUrl; - latestUpdateTS = [[NSDate date] timeIntervalSince1970] * 1000; + self->latestUpdateTS = [[NSDate date] timeIntervalSince1970] * 1000; success(); [self notifyListeners:nil]; diff --git a/MatrixSDK/Data/RoomList/Common/MXRoomListDataFilterable.swift b/MatrixSDK/Data/RoomList/Common/MXRoomListDataFilterable.swift new file mode 100644 index 0000000000..614ec2b500 --- /dev/null +++ b/MatrixSDK/Data/RoomList/Common/MXRoomListDataFilterable.swift @@ -0,0 +1,137 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation + +/// Protocol to be used in room list data managers to filter rooms +internal protocol MXRoomListDataFilterable { + + /// Filter options to use + var filterOptions: MXRoomListDataFilterOptions { get } + + /// Filter rooms in-memory + /// - Parameter rooms: rooms to filter + /// - Returns filtered rooms + func filterRooms(_ rooms: [MXRoomSummaryProtocol]) -> [MXRoomSummaryProtocol] + + /// Predicate to be used when filtering rooms + /// - Parameter filterOptions: filter options to create predicate + /// - Returns predicate + func filterPredicate(for filterOptions: MXRoomListDataFilterOptions) -> NSPredicate? + +} + +// MARK: - Default Implementation + +extension MXRoomListDataFilterable { + + /// Just to be used for in-memory data + func filterRooms(_ rooms: [MXRoomSummaryProtocol]) -> [MXRoomSummaryProtocol] { + guard let predicate = filterPredicate(for: filterOptions) else { + return rooms + } + + return (rooms as NSArray).filtered(using: predicate) as! [MXRoomSummaryProtocol] + } + + func filterPredicate(for filterOptions: MXRoomListDataFilterOptions) -> NSPredicate? { + var predicates: [NSPredicate] = [] + + if !filterOptions.onlySuggested { + if filterOptions.hideUnknownMembershipRooms { + let memberPredicate = NSPredicate(format: "%K != %d", + #keyPath(MXRoomSummaryProtocol.membership), + MXMembership.unknown.rawValue) + predicates.append(memberPredicate) + } + + if !filterOptions.dataTypes.isEmpty { + let predicate = NSPredicate(format: "(%K & %d) != 0", + #keyPath(MXRoomSummaryProtocol.dataTypes), + filterOptions.dataTypes.rawValue) + predicates.append(predicate) + } + + if !filterOptions.notDataTypes.isEmpty { + let predicate = NSPredicate(format: "(%K & %d) == 0", + #keyPath(MXRoomSummaryProtocol.dataTypes), + filterOptions.notDataTypes.rawValue) + predicates.append(predicate) + } + + if let space = filterOptions.space { + let predicate = NSPredicate(format: "%@ IN %K", + space.spaceId, + #keyPath(MXRoomSummaryProtocol.parentSpaceIds)) + predicates.append(predicate) + } else { + // home space + + // In case of home space we show a room if one of the following conditions is true: + // - Show All Rooms is enabled + // - It's a direct room + // - The room is a favourite + // - The room is orphaned + + let predicate1 = NSPredicate(value: filterOptions.showAllRoomsInHomeSpace) + + let directDataTypes: MXRoomSummaryDataTypes = .direct + let predicate2 = NSPredicate(format: "(%K & %d) != 0", + #keyPath(MXRoomSummaryProtocol.dataTypes), + directDataTypes.rawValue) + + let favoritedDataTypes: MXRoomSummaryDataTypes = .favorited + let predicate3 = NSPredicate(format: "(%K & %d) != 0", + #keyPath(MXRoomSummaryProtocol.dataTypes), + favoritedDataTypes.rawValue) + + let predicate4_1 = NSPredicate(format: "%K == NULL", + #keyPath(MXRoomSummaryProtocol.parentSpaceIds)) + let predicate4_2 = NSPredicate(format: "%K.@count == 0", + #keyPath(MXRoomSummaryProtocol.parentSpaceIds)) + let predicate4 = NSCompoundPredicate(type: .or, + subpredicates: [predicate4_1, predicate4_2]) + + let predicate = NSCompoundPredicate(type: .or, + subpredicates: [predicate1, predicate2, predicate3, predicate4]) + predicates.append(predicate) + } + } + + if let query = filterOptions.query, !query.isEmpty { + let predicate1 = NSPredicate(format: "%K CONTAINS[cd] %@", + #keyPath(MXRoomSummaryProtocol.displayname), + query) + let predicate2 = NSPredicate(format: "%K CONTAINS[cd] %@", + #keyPath(MXRoomSummaryProtocol.spaceChildInfo.displayName), + query) + let predicate = NSCompoundPredicate(type: .or, + subpredicates: [predicate1, predicate2]) + predicates.append(predicate) + } + + guard !predicates.isEmpty else { + return nil + } + + if predicates.count == 1 { + return predicates.first + } + return NSCompoundPredicate(type: .and, + subpredicates: predicates) + } + +} diff --git a/MatrixSDK/Data/RoomList/Common/MXRoomListDataSortable.swift b/MatrixSDK/Data/RoomList/Common/MXRoomListDataSortable.swift new file mode 100644 index 0000000000..ab696b3fe9 --- /dev/null +++ b/MatrixSDK/Data/RoomList/Common/MXRoomListDataSortable.swift @@ -0,0 +1,82 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation + +/// Protocol to be used in room list data managers to sort rooms +internal protocol MXRoomListDataSortable { + + /// Sort options to use + var sortOptions: MXRoomListDataSortOptions { get } + + /// Sort rooms in-memory + /// - Parameter rooms: rooms to sort + /// - Returns sorted rooms + func sortRooms(_ rooms: [MXRoomSummaryProtocol]) -> [MXRoomSummaryProtocol] + + /// Sort descriptors to be used when sorting rooms + /// - Parameter sortOptions: sort options to create descriptors + /// - Returns sort descriptors + func sortDescriptors(for sortOptions: MXRoomListDataSortOptions) -> [NSSortDescriptor] + +} + +// MARK: - Default Implementation + +extension MXRoomListDataSortable { + + func sortRooms(_ rooms: [MXRoomSummaryProtocol]) -> [MXRoomSummaryProtocol] { + let descriptors = sortDescriptors(for: sortOptions) + return (rooms as NSArray).sortedArray(using: descriptors) as! [MXRoomSummaryProtocol] + } + + func sortDescriptors(for sortOptions: MXRoomListDataSortOptions) -> [NSSortDescriptor] { + var result: [NSSortDescriptor] = [] + + // TODO: reintroduce order once it will be supported +// if sortOptions.suggested { +// result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.spaceChildInfo?.order, ascending: false)) +// } + + if sortOptions.invitesFirst { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.membership, ascending: true)) + } + + if sortOptions.sentStatus { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.sentStatus, ascending: false)) + } + + if sortOptions.missedNotificationsFirst { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.hasAnyHighlight, ascending: false)) + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.hasAnyNotification, ascending: false)) + } + + if sortOptions.unreadMessagesFirst { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.hasAnyUnread, ascending: false)) + } + + if sortOptions.lastEventDate { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.lastMessage?.originServerTs, ascending: false)) + } + + if sortOptions.favoriteTag { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.favoriteTagOrder, ascending: false)) + } + + return result + } + +} diff --git a/MatrixSDK/Data/RoomList/Common/MXSuggestedRoomListDataFetcher.swift b/MatrixSDK/Data/RoomList/Common/MXSuggestedRoomListDataFetcher.swift index 0a54251098..376723c0b0 100644 --- a/MatrixSDK/Data/RoomList/Common/MXSuggestedRoomListDataFetcher.swift +++ b/MatrixSDK/Data/RoomList/Common/MXSuggestedRoomListDataFetcher.swift @@ -19,7 +19,7 @@ import Foundation @objcMembers internal class MXSuggestedRoomListDataFetcher: NSObject, MXRoomListDataFetcher { - public let fetchOptions: MXRoomListDataFetchOptions + internal let fetchOptions: MXRoomListDataFetchOptions private let spaceService: MXSpaceService private let cache: MXSuggestedRoomListDataCache @@ -35,7 +35,7 @@ internal class MXSuggestedRoomListDataFetcher: NSObject, MXRoomListDataFetcher { private var spaceEventsListener: Any? private var currentHttpOperation: MXHTTPOperation? - public private(set) var data: MXRoomListData? { + internal private(set) var data: MXRoomListData? { didSet { guard let data = data else { // do not notify when stopped @@ -61,19 +61,19 @@ internal class MXSuggestedRoomListDataFetcher: NSObject, MXRoomListDataFetcher { // MARK: - Delegate - public func addDelegate(_ delegate: MXRoomListDataFetcherDelegate) { + internal func addDelegate(_ delegate: MXRoomListDataFetcherDelegate) { multicastDelegate.addDelegate(delegate) } - public func removeDelegate(_ delegate: MXRoomListDataFetcherDelegate) { + internal func removeDelegate(_ delegate: MXRoomListDataFetcherDelegate) { multicastDelegate.removeDelegate(delegate) } - public func removeAllDelegates() { + internal func removeAllDelegates() { multicastDelegate.removeAllDelegates() } - private func notifyDataChange() { + internal func notifyDataChange() { multicastDelegate.invoke({ $0.fetcherDidChangeData(self) }) } @@ -94,7 +94,7 @@ internal class MXSuggestedRoomListDataFetcher: NSObject, MXRoomListDataFetcher { space?.room?.removeListener(spaceEventsListener) } - func paginate() { + internal func paginate() { let numberOfItems: Int if let data = data { @@ -118,11 +118,11 @@ internal class MXSuggestedRoomListDataFetcher: NSObject, MXRoomListDataFetcher { computeData(upto: numberOfItems) } - func resetPagination() { + internal func resetPagination() { computeData(upto: fetchOptions.paginationOptions.rawValue) } - func refresh() { + internal func refresh() { // cancel current request currentHttpOperation?.cancel() currentHttpOperation = nil @@ -135,7 +135,7 @@ internal class MXSuggestedRoomListDataFetcher: NSObject, MXRoomListDataFetcher { } } - func stop() { + internal func stop() { removeAllDelegates() removeDataObservers(for: space) data = nil @@ -202,13 +202,33 @@ internal class MXSuggestedRoomListDataFetcher: NSObject, MXRoomListDataFetcher { private func computeData(from childInfos: [MXSpaceChildInfo]) { // create room summary objects var rooms: [MXRoomSummaryProtocol] = childInfos.map({ MXRoomSummary(spaceChildInfo: $0) }) - rooms = fetchOptions.filterOptions.filterRooms(rooms) - rooms = fetchOptions.sortOptions.sortRooms(rooms) + rooms = filterRooms(rooms) + rooms = sortRooms(rooms) // we don't know total rooms count, passing as current number of rooms self.data = MXRoomListData(rooms: rooms, counts: MXStoreRoomListDataCounts(withRooms: rooms, - totalRoomsCount: rooms.count), + total: nil), paginationOptions: fetchOptions.paginationOptions) } } + +// MARK: MXRoomListDataSortable + +extension MXSuggestedRoomListDataFetcher: MXRoomListDataSortable { + + var sortOptions: MXRoomListDataSortOptions { + return fetchOptions.sortOptions + } + +} + +// MARK: MXRoomListDataFilterable + +extension MXSuggestedRoomListDataFetcher: MXRoomListDataFilterable { + + var filterOptions: MXRoomListDataFilterOptions { + return fetchOptions.filterOptions + } + +} diff --git a/MatrixSDK/Data/RoomList/CoreData/CoreDataContextable.swift b/MatrixSDK/Data/RoomList/CoreData/CoreDataContextable.swift new file mode 100644 index 0000000000..2dd104991a --- /dev/null +++ b/MatrixSDK/Data/RoomList/CoreData/CoreDataContextable.swift @@ -0,0 +1,26 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +internal protocol CoreDataContextable { + + /// Managed object context to be used for main thread applications. + /// Implementations should return a context with `.mainQueueConcurrencyType` concurrency type + var mainManagedObjectContext: NSManagedObjectContext { get } + +} diff --git a/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift new file mode 100644 index 0000000000..f7d9df8c68 --- /dev/null +++ b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataFetcher.swift @@ -0,0 +1,374 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +internal typealias MXRoomSummaryCoreDataContextableStore = MXRoomSummaryStore & CoreDataContextable + +@objcMembers +internal class MXCoreDataRoomListDataFetcher: NSObject, MXRoomListDataFetcher { + + private let multicastDelegate: MXMulticastDelegate = MXMulticastDelegate() + + private weak var session: MXSession? + internal let fetchOptions: MXRoomListDataFetchOptions + private lazy var dataUpdateThrottler: MXThrottler = { + return MXThrottler(minimumDelay: 0.1, queue: .main) + }() + + internal private(set) var data: MXRoomListData? { + didSet { + guard let data = data else { + // do not notify when stopped + return + } + if data != oldValue { + notifyDataChange() + } + } + } + private let store: MXRoomSummaryCoreDataContextableStore + + private lazy var fetchedResultsController: NSFetchedResultsController = { + let request = MXRoomSummaryMO.typedFetchRequest() + request.predicate = filterPredicate(for: filterOptions) + request.sortDescriptors = sortDescriptors(for: sortOptions) + request.fetchLimit = fetchOptions.paginationOptions.rawValue + return NSFetchedResultsController(fetchRequest: request, + managedObjectContext: store.mainManagedObjectContext, + sectionNameKeyPath: nil, + cacheName: nil) + }() + + private var totalRoomsCount: Int { + let request = MXRoomSummaryMO.typedFetchRequest() + request.predicate = filterPredicate(for: filterOptions) + request.resultType = .countResultType + do { + return try store.mainManagedObjectContext.count(for: request) + } catch let error { + MXLog.error("[MXCoreDataRoomListDataFetcher] failed to count rooms: \(error)") + return 0 + } + } + + private var totalCounts: MXRoomListDataCounts? { + guard fetchOptions.paginationOptions != .none else { + return nil + } + let request = MXRoomSummaryMO.typedFetchRequest() + request.predicate = filterPredicate(for: filterOptions) + let propertyNames: [String] = [ + "s_dataTypesInt", + "s_sentStatusInt", + "s_notificationCount", + "s_highlightCount" + ] + var properties: [NSPropertyDescription] = [] + + for propertyName in propertyNames { + guard let property = MXRoomSummaryMO.entity().propertiesByName[propertyName] else { + fatalError("[MXCoreDataRoomSummaryStore] Couldn't find \(propertyName) on entity \(String(describing: MXRoomSummaryMO.self)), probably property name changed") + } + properties.append(property) + } + request.propertiesToFetch = properties + do { + let summaries = try store.mainManagedObjectContext.fetch(request) + return MXStoreRoomListDataCounts(withRooms: summaries, + total: nil) + } catch let error { + MXLog.error("[MXCoreDataRoomListDataFetcher] failed to calculate total counts: \(error)") + return nil + } + } + + internal init(session: MXSession?, + fetchOptions: MXRoomListDataFetchOptions, + store: MXRoomSummaryCoreDataContextableStore) { + self.session = session + self.fetchOptions = fetchOptions + self.store = store + super.init() + self.fetchOptions.fetcher = self + self.fetchedResultsController.delegate = self + } + + // MARK: - Delegate + + internal func addDelegate(_ delegate: MXRoomListDataFetcherDelegate) { + multicastDelegate.addDelegate(delegate) + } + + internal func removeDelegate(_ delegate: MXRoomListDataFetcherDelegate) { + multicastDelegate.removeDelegate(delegate) + } + + internal func removeAllDelegates() { + multicastDelegate.removeAllDelegates() + } + + // MARK: - Data + + func paginate() { + guard let oldData = data else { + // load first page + performFetch() + return + } + + guard oldData.hasMoreRooms else { + // no more rooms + return + } + removeCacheIfRequired() + let numberOfItems = (oldData.currentPage + 2) * oldData.paginationOptions.rawValue + fetchedResultsController.fetchRequest.fetchLimit = numberOfItems > 0 ? numberOfItems : 0 + performFetch() + } + + func resetPagination() { + removeCacheIfRequired() + let numberOfItems = fetchOptions.paginationOptions.rawValue + fetchedResultsController.fetchRequest.fetchLimit = numberOfItems > 0 ? numberOfItems : 0 + performFetch() + } + + func refresh() { + guard let oldData = data else { + // data will still be nil if the fetchRequest properties have changed before fetching the first page. + // In this instance, update the fetchRequest so it is correctly configured for the first page fetch. + fetchedResultsController.fetchRequest.predicate = filterPredicate(for: filterOptions) + fetchedResultsController.fetchRequest.sortDescriptors = sortDescriptors(for: sortOptions) + fetchedResultsController.fetchRequest.fetchLimit = fetchOptions.paginationOptions.rawValue + return + } + data = nil + recomputeData(using: oldData) + } + + func stop() { + fetchedResultsController.delegate = nil + removeCacheIfRequired() + } + + deinit { + stop() + } + + // MARK: - Private + + private func removeCacheIfRequired() { + if let cacheName = fetchedResultsController.cacheName { + NSFetchedResultsController.deleteCache(withName: cacheName) + } + } + + private func performFetch() { + do { + try fetchedResultsController.performFetch() + computeData() + } catch let error { + MXLog.error("[MXCoreDataRoomListDataFetcher] failed to perform fetch: \(error)") + } + } + + private func notifyDataChange() { + multicastDelegate.invoke({ $0.fetcherDidChangeData(self) }) + } + + /// Recompute data with the same number of rooms of the given `data` + private func recomputeData(using data: MXRoomListData) { + removeCacheIfRequired() + let numberOfItems = (data.currentPage + 1) * data.paginationOptions.rawValue + fetchedResultsController.fetchRequest.predicate = filterPredicate(for: filterOptions) + fetchedResultsController.fetchRequest.sortDescriptors = sortDescriptors(for: sortOptions) + fetchedResultsController.fetchRequest.fetchLimit = numberOfItems > 0 ? numberOfItems : 0 + performFetch() + } + + private func computeData() { + guard let summaries = fetchedResultsController.fetchedObjects else { + data = nil + return + } + + let fetchLimit = fetchedResultsController.fetchRequest.fetchLimit + let mapped: [MXRoomSummary] + + if fetchLimit > 0 && summaries.count > fetchLimit { + data = nil + mapped = summaries[0.. [NSSortDescriptor] { + var result: [NSSortDescriptor] = [] + + if sortOptions.invitesFirst { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryMO.s_membershipInt, ascending: true)) + } + + if sortOptions.sentStatus { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryMO.s_sentStatusInt, ascending: false)) + } + + if sortOptions.missedNotificationsFirst { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryMO.s_hasAnyHighlight, ascending: false)) + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryMO.s_hasAnyNotification, ascending: false)) + } + + if sortOptions.unreadMessagesFirst { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryMO.s_hasAnyUnread, ascending: false)) + } + + if sortOptions.lastEventDate { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryMO.s_lastMessage?.s_originServerTs, ascending: false)) + } + + if sortOptions.favoriteTag { + result.append(NSSortDescriptor(keyPath: \MXRoomSummaryMO.s_favoriteTagOrder, ascending: false)) + } + + return result + } + +} + +// MARK: MXRoomListDataFilterable + +extension MXCoreDataRoomListDataFetcher: MXRoomListDataFilterable { + + var filterOptions: MXRoomListDataFilterOptions { + return fetchOptions.filterOptions + } + + func filterPredicate(for filterOptions: MXRoomListDataFilterOptions) -> NSPredicate? { + var predicates: [NSPredicate] = [] + + if !filterOptions.onlySuggested { + if filterOptions.hideUnknownMembershipRooms { + let memberPredicate = NSPredicate(format: "%K != %d", + #keyPath(MXRoomSummaryMO.s_membershipInt), + MXMembership.unknown.rawValue) + predicates.append(memberPredicate) + } + + // data types + if !filterOptions.dataTypes.isEmpty { + let predicate = NSPredicate(format: "(%K & %d) != 0", + #keyPath(MXRoomSummaryMO.s_dataTypesInt), + filterOptions.dataTypes.rawValue) + predicates.append(predicate) + } + + // not data types + if !filterOptions.notDataTypes.isEmpty { + let predicate = NSPredicate(format: "(%K & %d) == 0", + #keyPath(MXRoomSummaryMO.s_dataTypesInt), + filterOptions.notDataTypes.rawValue) + predicates.append(predicate) + } + + // space + if let space = filterOptions.space { + // specific space + let predicate = NSPredicate(format: "%K CONTAINS[c] %@", + #keyPath(MXRoomSummaryMO.s_parentSpaceIds), + space.spaceId) + predicates.append(predicate) + } else { + // home space + + // In case of home space we show a room if one of the following conditions is true: + // - Show All Rooms is enabled + // - It's a direct room + // - The room is a favourite + // - The room is orphaned + + let predicate1 = NSPredicate(value: filterOptions.showAllRoomsInHomeSpace) + + let directDataTypes: MXRoomSummaryDataTypes = .direct + let predicate2 = NSPredicate(format: "(%K & %d) != 0", + #keyPath(MXRoomSummaryMO.s_dataTypesInt), + directDataTypes.rawValue) + + let favoritedDataTypes: MXRoomSummaryDataTypes = .favorited + let predicate3 = NSPredicate(format: "(%K & %d) != 0", + #keyPath(MXRoomSummaryMO.s_dataTypesInt), + favoritedDataTypes.rawValue) + + let predicate4 = NSPredicate(format: "%K MATCHES %@", + #keyPath(MXRoomSummaryMO.s_parentSpaceIds), + "^$") + + let predicate = NSCompoundPredicate(type: .or, + subpredicates: [predicate1, predicate2, predicate3, predicate4]) + predicates.append(predicate) + } + } + + // query + if let query = filterOptions.query, !query.isEmpty { + let predicate = NSPredicate(format: "%K CONTAINS[cd] %@", + #keyPath(MXRoomSummaryMO.s_displayName), + query) + predicates.append(predicate) + } + + guard !predicates.isEmpty else { + return nil + } + + if predicates.count == 1 { + return predicates.first + } + return NSCompoundPredicate(type: .and, + subpredicates: predicates) + } + +} + +// MARK: - NSFetchedResultsControllerDelegate + +extension MXCoreDataRoomListDataFetcher: NSFetchedResultsControllerDelegate { + + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + dataUpdateThrottler.throttle { + self.computeData() + } + } + +} diff --git a/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataManager.swift b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataManager.swift new file mode 100644 index 0000000000..82cce77940 --- /dev/null +++ b/MatrixSDK/Data/RoomList/CoreData/MXCoreDataRoomListDataManager.swift @@ -0,0 +1,52 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation + +@objcMembers +public class MXCoreDataRoomListDataManager: NSObject, MXRoomListDataManager { + public weak var session: MXSession? + + public func configure(withSession session: MXSession) { + assert(self.session == nil, "[MXCoreDataRoomListDataManager] Cannot configure the session again") + self.session = session + } + + public func fetcher(withOptions options: MXRoomListDataFetchOptions) -> MXRoomListDataFetcher { + assert(options.async, "[MXCoreDataRoomListDataManager] cannot work with sync fetch options") + + if options.filterOptions.onlySuggested { + guard let spaceService = session?.spaceService else { + fatalError("[MXCoreDataRoomListDataManager] Session has no spaceService") + } + return MXSuggestedRoomListDataFetcher(fetchOptions: options, + spaceService: spaceService) + } + guard let session = session, let store = session.store else { + fatalError("[MXCoreDataRoomListDataManager] No session or no store") + } + guard let coreDataStore = store.roomSummaryStore as? MXRoomSummaryCoreDataContextableStore else { + fatalError("[MXCoreDataRoomListDataManager] Session.store.roomSummaryStore is not CoreDataContextable") + } + + assert(coreDataStore.mainManagedObjectContext.concurrencyType == .mainQueueConcurrencyType, + "[MXCoreDataRoomListDataManager] Managed object context must have mainQueueConcurrencyType") + + return MXCoreDataRoomListDataFetcher(session: session, + fetchOptions: options, + store: coreDataStore) + } +} diff --git a/MatrixSDK/Data/RoomList/MXRoomListData.swift b/MatrixSDK/Data/RoomList/MXRoomListData.swift index 486de9d15f..fd8c49e4f9 100644 --- a/MatrixSDK/Data/RoomList/MXRoomListData.swift +++ b/MatrixSDK/Data/RoomList/MXRoomListData.swift @@ -36,7 +36,8 @@ open class MXRoomListData: NSObject { /// Flag to indicate whether more rooms exist in next pages public var hasMoreRooms: Bool { - return counts.numberOfRooms < counts.totalRoomsCount + let totalNumberOfRooms = counts.total?.numberOfRooms ?? 0 + return counts.numberOfRooms < totalNumberOfRooms } /// Get room at index @@ -77,7 +78,9 @@ open class MXRoomListData: NSObject { let roomsHash = rooms.reduce(1, { $0 ^ $1.hash }).hashValue result = prime * result + Int64(roomsHash) result = prime * result + Int64(paginationOptions.rawValue) - result = prime * result + Int64(counts.totalRoomsCount) + if let total = counts.total { + result = prime * result + Int64(total.numberOfRooms) + } return String(result).hash } diff --git a/MatrixSDK/Data/RoomList/MXRoomListDataCounts.swift b/MatrixSDK/Data/RoomList/MXRoomListDataCounts.swift index dbb78830ec..13e37233e1 100644 --- a/MatrixSDK/Data/RoomList/MXRoomListDataCounts.swift +++ b/MatrixSDK/Data/RoomList/MXRoomListDataCounts.swift @@ -41,7 +41,7 @@ public protocol MXRoomListDataCounts { /// Number of invited rooms for handled rooms var numberOfInvitedRooms: Int { get } - /// Total number of rooms. Can be different from `numberOfRooms` if pagination enabled - var totalRoomsCount: Int { get } + /// Total values. nil if pagination is not enabled. + var total: MXRoomListDataCounts? { get } } diff --git a/MatrixSDK/Data/RoomList/MXRoomListDataFetchOptions.swift b/MatrixSDK/Data/RoomList/MXRoomListDataFetchOptions.swift index 586623e317..5e4ce1a1f1 100644 --- a/MatrixSDK/Data/RoomList/MXRoomListDataFetchOptions.swift +++ b/MatrixSDK/Data/RoomList/MXRoomListDataFetchOptions.swift @@ -52,7 +52,7 @@ public final class MXRoomListDataFetchOptions: NSObject { /// - async: flag indicating the fetch should be performed in async public init(filterOptions: MXRoomListDataFilterOptions, sortOptions: MXRoomListDataSortOptions, - paginationOptions: MXRoomListDataPaginationOptions = .none, + paginationOptions: MXRoomListDataPaginationOptions = .default, async: Bool = true) { self.filterOptions = filterOptions self.sortOptions = sortOptions diff --git a/MatrixSDK/Data/RoomList/MXRoomListDataFilterOptions.swift b/MatrixSDK/Data/RoomList/MXRoomListDataFilterOptions.swift index 56bd499e98..f82d1bc487 100644 --- a/MatrixSDK/Data/RoomList/MXRoomListDataFilterOptions.swift +++ b/MatrixSDK/Data/RoomList/MXRoomListDataFilterOptions.swift @@ -48,7 +48,10 @@ public struct MXRoomListDataFilterOptions: Equatable { /// - Parameters: /// - dataTypes: data types to fetch. Pass `MXRoomListDataFilterOptions.emptyDataTypes` not to specify any. /// - notDataTypes: data types not to fetch. Pass `MXRoomListDataFilterOptions.emptyDataTypes` not to specify any. + /// - onlySuggested: flag to filter only suggested rooms. Only `space` and `query` parameters are honored if true. /// - query: search query + /// - space: active space + /// - showAllRoomsInHomeSpace: flag to show all rooms in home space (when `space` is not provided) public init(dataTypes: MXRoomSummaryDataTypes = MXRoomListDataFilterOptions.emptyDataTypes, notDataTypes: MXRoomSummaryDataTypes = [.hidden, .conferenceUser, .space], onlySuggested: Bool = false, @@ -64,93 +67,4 @@ public struct MXRoomListDataFilterOptions: Equatable { self.showAllRoomsInHomeSpace = showAllRoomsInHomeSpace self.hideUnknownMembershipRooms = hideUnknownMembershipRooms } - - /// Just to be used for in-memory data - internal func filterRooms(_ rooms: [MXRoomSummaryProtocol]) -> [MXRoomSummaryProtocol] { - guard let predicate = predicate else { - return rooms - } - - return (rooms as NSArray).filtered(using: predicate) as! [MXRoomSummaryProtocol] - } - - /// To be used for CoreData fetch request - internal var predicate: NSPredicate? { - var subpredicates: [NSPredicate] = [] - - if let query = query, !query.isEmpty { - let subpredicate1 = NSPredicate(format: "%K CONTAINS[cd] %@", - #keyPath(MXRoomSummaryProtocol.displayname), query) - let subpredicate2 = NSPredicate(format: "%K CONTAINS[cd] %@", - #keyPath(MXRoomSummaryProtocol.spaceChildInfo.displayName), query) - let subpredicate = NSCompoundPredicate(type: .or, - subpredicates: [subpredicate1, subpredicate2]) - subpredicates.append(subpredicate) - } - - if !onlySuggested { - if hideUnknownMembershipRooms { - let memberPredicate = NSPredicate(format: "%K != %d", - #keyPath(MXRoomSummaryProtocol.membership), MXMembership.unknown.rawValue) - subpredicates.append(memberPredicate) - } - - if !dataTypes.isEmpty { - let subpredicate = NSPredicate(format: "(%K & %d) != 0", - #keyPath(MXRoomSummaryProtocol.dataTypes), dataTypes.rawValue) - subpredicates.append(subpredicate) - } - - if !notDataTypes.isEmpty { - let subpredicate = NSPredicate(format: "(%K & %d) == 0", - #keyPath(MXRoomSummaryProtocol.dataTypes), notDataTypes.rawValue) - subpredicates.append(subpredicate) - } - - if let space = space { - let subpredicate = NSPredicate(format: "%@ IN %K", space.spaceId, - #keyPath(MXRoomSummaryProtocol.parentSpaceIds)) - subpredicates.append(subpredicate) - } else { - // home space - - // In case of home space we show a room if one of the following conditions is true: - // - Show All Rooms is enabled - // - It's a direct room - // - The room is a favourite - // - The room is orphaned - - let subpredicate1 = NSPredicate(value: showAllRoomsInHomeSpace) - - let directDataTypes: MXRoomSummaryDataTypes = .direct - let subpredicate2 = NSPredicate(format: "(%K & %d) != 0", - #keyPath(MXRoomSummaryProtocol.dataTypes), directDataTypes.rawValue) - - let favoritedDataTypes: MXRoomSummaryDataTypes = .favorited - let subpredicate3 = NSPredicate(format: "(%K & %d) != 0", - #keyPath(MXRoomSummaryProtocol.dataTypes), favoritedDataTypes.rawValue) - - let subpredicate4_1 = NSPredicate(format: "%K == NULL", - #keyPath(MXRoomSummaryProtocol.parentSpaceIds)) - let subpredicate4_2 = NSPredicate(format: "%K.@count == 0", - #keyPath(MXRoomSummaryProtocol.parentSpaceIds)) - let subpredicate4 = NSCompoundPredicate(type: .or, - subpredicates: [subpredicate4_1, subpredicate4_2]) - - let subpredicate = NSCompoundPredicate(type: .or, - subpredicates: [subpredicate1, subpredicate2, subpredicate3, subpredicate4]) - subpredicates.append(subpredicate) - } - } - - guard !subpredicates.isEmpty else { - return nil - } - - if subpredicates.count == 1 { - return subpredicates.first - } - return NSCompoundPredicate(type: .and, - subpredicates: subpredicates) - } } diff --git a/MatrixSDK/Data/RoomList/MXRoomListDataSortOptions.swift b/MatrixSDK/Data/RoomList/MXRoomListDataSortOptions.swift index 470c55b349..c790e45e51 100644 --- a/MatrixSDK/Data/RoomList/MXRoomListDataSortOptions.swift +++ b/MatrixSDK/Data/RoomList/MXRoomListDataSortOptions.swift @@ -68,46 +68,4 @@ public struct MXRoomListDataSortOptions: Equatable { self.missedNotificationsFirst = missedNotificationsFirst self.unreadMessagesFirst = unreadMessagesFirst } - - /// Just to be used for in-memory data - internal func sortRooms(_ rooms: [MXRoomSummaryProtocol]) -> [MXRoomSummaryProtocol] { - return (rooms as NSArray).sortedArray(using: sortDescriptors) as! [MXRoomSummaryProtocol] - } - - /// To be used for CoreData fetch request - internal var sortDescriptors: [NSSortDescriptor] { - var result: [NSSortDescriptor] = [] - - // TODO: reintroduce order once it will be supported -// if suggested { -// result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.spaceChildInfo?.order, ascending: false)) -// } - - if invitesFirst { - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.membership, ascending: true)) - } - - if sentStatus { - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.sentStatus, ascending: false)) - } - - if missedNotificationsFirst { - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.hasAnyHighlight, ascending: false)) - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.hasAnyNotification, ascending: false)) - } - - if unreadMessagesFirst { - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.hasAnyUnread, ascending: false)) - } - - if lastEventDate { - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.lastMessage?.originServerTs, ascending: false)) - } - - if favoriteTag { - result.append(NSSortDescriptor(keyPath: \MXRoomSummaryProtocol.favoriteTagOrder, ascending: false)) - } - - return result - } } diff --git a/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataCounts.swift b/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataCounts.swift index 6a600083e4..8a1b2b89f9 100644 --- a/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataCounts.swift +++ b/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataCounts.swift @@ -26,16 +26,16 @@ public class MXStoreRoomListDataCounts: NSObject, MXRoomListDataCounts { public let numberOfNotifications: UInt public let numberOfHighlights: UInt public let numberOfInvitedRooms: Int - public let totalRoomsCount: Int + public var total: MXRoomListDataCounts? public init(withRooms rooms: [MXRoomSummaryProtocol], - totalRoomsCount: Int) { - var numberOfInvitedRooms: Int = 0 + total: MXRoomListDataCounts?) { var numberOfUnsentRooms: Int = 0 var numberOfNotifiedRooms: Int = 0 var numberOfHighlightedRooms: Int = 0 var numberOfNotifications: UInt = 0 var numberOfHighlights: UInt = 0 + var numberOfInvitedRooms: Int = 0 rooms.forEach { summary in if summary.isTyped(.invited) { @@ -55,13 +55,13 @@ public class MXStoreRoomListDataCounts: NSObject, MXRoomListDataCounts { } self.numberOfRooms = rooms.count - self.numberOfInvitedRooms = numberOfInvitedRooms self.numberOfUnsentRooms = numberOfUnsentRooms self.numberOfNotifiedRooms = numberOfNotifiedRooms + numberOfInvitedRooms self.numberOfHighlightedRooms = numberOfHighlightedRooms self.numberOfNotifications = numberOfNotifications self.numberOfHighlights = numberOfHighlights - self.totalRoomsCount = totalRoomsCount + self.numberOfInvitedRooms = numberOfInvitedRooms + self.total = total super.init() } diff --git a/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataFetcher.swift b/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataFetcher.swift index e4ea173a28..cb5e4fb5f6 100644 --- a/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataFetcher.swift +++ b/MatrixSDK/Data/RoomList/MXStore/MXStoreRoomListDataFetcher.swift @@ -18,7 +18,7 @@ import Foundation @objcMembers internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { - public private(set) var data: MXRoomListData? { + internal private(set) var data: MXRoomListData? { didSet { guard let data = data else { // do not notify when stopped @@ -29,7 +29,7 @@ internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { } } } - public let fetchOptions: MXRoomListDataFetchOptions + internal let fetchOptions: MXRoomListDataFetchOptions private let store: MXRoomSummaryStore private let multicastDelegate: MXMulticastDelegate = MXMulticastDelegate() @@ -47,21 +47,21 @@ internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { // MARK: - Delegate - public func addDelegate(_ delegate: MXRoomListDataFetcherDelegate) { + internal func addDelegate(_ delegate: MXRoomListDataFetcherDelegate) { multicastDelegate.addDelegate(delegate) } - public func removeDelegate(_ delegate: MXRoomListDataFetcherDelegate) { + internal func removeDelegate(_ delegate: MXRoomListDataFetcherDelegate) { multicastDelegate.removeDelegate(delegate) } - public func removeAllDelegates() { + internal func removeAllDelegates() { multicastDelegate.removeAllDelegates() } // MARK: - Data - public func paginate() { + internal func paginate() { if fetchOptions.async { executionQueue.async { [weak self] in guard let self = self else { return } @@ -75,7 +75,7 @@ internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { } } - public func resetPagination() { + internal func resetPagination() { if fetchOptions.async { executionQueue.async { [weak self] in guard let self = self else { return } @@ -89,7 +89,7 @@ internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { } } - public func refresh() { + internal func refresh() { guard let oldData = data else { return } @@ -97,7 +97,7 @@ internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { recomputeData(using: oldData) } - public func stop() { + internal func stop() { removeAllDelegates() removeDataObservers() data = nil @@ -110,7 +110,7 @@ internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { if let data = data { // load next page - if data.counts.numberOfRooms == data.counts.totalRoomsCount { + guard data.hasMoreRooms else { // there is no more rooms to paginate return } @@ -146,16 +146,20 @@ internal class MXStoreRoomListDataFetcher: NSObject, MXRoomListDataFetcher { /// Compute data up to a numberOfItems private func computeData(upto numberOfItems: Int) -> MXRoomListData { var rooms = Array(roomSummaries.values) - rooms = fetchOptions.filterOptions.filterRooms(rooms) - rooms = fetchOptions.sortOptions.sortRooms(rooms) + rooms = filterRooms(rooms) + rooms = sortRooms(rooms) + + var total: MXRoomListDataCounts? - let totalRoomsCount = rooms.count if numberOfItems > 0 && rooms.count > numberOfItems { + // compute total counts just before cutting the rooms array + total = MXStoreRoomListDataCounts(withRooms: rooms, total: nil) rooms = Array(rooms[0.. MXRoomListDataFetcher { if options.filterOptions.onlySuggested { guard let spaceService = session?.spaceService else { - fatalError("Session has no spaceService") + fatalError("[MXStoreRoomListDataManager] Session has no spaceService") } return MXSuggestedRoomListDataFetcher(fetchOptions: options, spaceService: spaceService) } guard let store = session?.store else { - fatalError("Session has no store") + fatalError("[MXStoreRoomListDataManager] Session has no store") } return MXStoreRoomListDataFetcher(fetchOptions: options, - store: store) + store: store.roomSummaryStore) } } diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.swift new file mode 100644 index 0000000000..82b3b5960e --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.swift @@ -0,0 +1,319 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +@objcMembers +public class MXCoreDataRoomSummaryStore: NSObject { + + private enum Constants { + static let modelName: String = "MXCoreDataRoomSummaryStore" + static let folderName: String = "MXCoreDataRoomSummaryStore" + static let storeFileName: String = "RoomSummaryStore.sqlite" + } + + private let credentials: MXCredentials + + private lazy var persistenceCoordinator: NSPersistentStoreCoordinator = { + let result = NSPersistentStoreCoordinator(managedObjectModel: Self.managedObjectModel) + do { + try result.addPersistentStore(ofType: NSSQLiteStoreType, + configurationName: nil, + at: storeURL, + options: nil) + } catch { + fatalError(error.localizedDescription) + } + return result + }() + private lazy var storeURL: URL = { + guard let userId = credentials.userId else { + fatalError("[MXCoreDataRoomSummaryStore] Credentials must provide a user identifier") + } + + var cachePath: URL! + if let appGroupIdentifier = MXSDKOptions.sharedInstance().applicationGroupIdentifier { + cachePath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) + } else { + cachePath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first + } + let folderUrl = cachePath.appendingPathComponent(Constants.folderName).appendingPathComponent(userId) + try? FileManager.default.createDirectory(at: folderUrl, withIntermediateDirectories: true, attributes: nil) + return folderUrl.appendingPathComponent(Constants.storeFileName) + }() + private static var managedObjectModel: NSManagedObjectModel = { + guard let url = Bundle(for: MXCoreDataRoomSummaryStore.self).url(forResource: Constants.modelName, + withExtension: "momd") else { + fatalError("[MXCoreDataRoomSummaryStore] No MXRoomSummaryStore Core Data model") + } + guard let result = NSManagedObjectModel(contentsOf: url) else { + fatalError("[MXCoreDataRoomSummaryStore] Cannot create managed object model") + } + return result + }() + + /// Managed object context to be used when inserting data, whose parent context is `mainMoc`. + private lazy var backgroundMoc: NSManagedObjectContext = { + let result = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + result.parent = mainMoc + return result + }() + /// Managed object context to be used on main thread for fetching data, whose parent context is `persistentMoc`. + private lazy var mainMoc: NSManagedObjectContext = { + let result = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) + result.parent = persistentMoc + return result + }() + /// Managed object context bound to persistent store coordinator. + private lazy var persistentMoc: NSManagedObjectContext = { + let result = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + result.persistentStoreCoordinator = persistenceCoordinator + return result + }() + + public init(withCredentials credentials: MXCredentials) { + self.credentials = credentials + super.init() + // create main context + _ = mainMoc + } + + // MARK: - Private + + private func countRooms(in moc: NSManagedObjectContext) -> Int { + let request = MXRoomSummaryMO.typedFetchRequest() + request.includesSubentities = false + request.includesPropertyValues = false + // fetch nothing + request.propertiesToFetch = [] + request.resultType = .countResultType + var result = 0 + moc.performAndWait { + do { + result = try moc.count(for: request) + } catch { + MXLog.error("[MXCoreDataRoomSummaryStore] countRooms failed: \(error)") + } + } + return result + } + + private func fetchRoomIds(in moc: NSManagedObjectContext) -> [String] { + let propertyName = "s_identifier" + + guard let property = MXRoomSummaryMO.entity().propertiesByName[propertyName] else { + fatalError("[MXCoreDataRoomSummaryStore] Couldn't find \(propertyName) on entity \(String(describing: MXRoomSummaryMO.self)), probably property name changed") + } + let request = MXRoomSummaryMO.typedFetchRequest() + request.includesSubentities = false + // only fetch room identifiers + request.propertiesToFetch = [property] + var result: [String] = [] + moc.performAndWait { + do { + let results = try moc.fetch(request) + // do not attempt to access other properties from the results + result = results.map({ $0.s_identifier }) + } catch { + MXLog.error("[MXCoreDataRoomSummaryStore] fetchRoomIds failed: \(error)") + } + } + return result + } + + private func fetchSummary(forRoomId roomId: String, in moc: NSManagedObjectContext) -> MXRoomSummary? { + let request = MXRoomSummaryMO.typedFetchRequest() + request.predicate = NSPredicate(format: "%K == %@", + #keyPath(MXRoomSummaryMO.s_identifier), + roomId) + var result: MXRoomSummary? = nil + moc.performAndWait { + do { + let results = try moc.fetch(request) + if let model = results.first { + result = MXRoomSummary(summaryModel: model) + } + } catch { + MXLog.error("[MXCoreDataRoomSummaryStore] fetchSummary failed: \(error)") + } + } + return result + } + + /// Inline method to fetch a summary managed object. Only to be called in moc.perform blocks. + private func fetchSummaryMO(forRoomId roomId: String, in moc: NSManagedObjectContext) -> MXRoomSummaryMO? { + let request = MXRoomSummaryMO.typedFetchRequest() + request.predicate = NSPredicate(format: "%K == %@", + #keyPath(MXRoomSummaryMO.s_identifier), + roomId) + do { + let results = try moc.fetch(request) + return results.first + } catch { + MXLog.error("[MXCoreDataRoomSummaryStore] fetchSummary failed: \(error)") + } + return nil + } + + private func saveSummary(_ summary: MXRoomSummaryProtocol) { + let moc = backgroundMoc + + moc.perform { [weak self] in + guard let self = self else { return } + if let existing = self.fetchSummaryMO(forRoomId: summary.roomId, in: moc) { + existing.update(withRoomSummary: summary, in: moc) + } else { + let model = MXRoomSummaryMO.insert(roomSummary: summary, into: moc) + do { + try moc.obtainPermanentIDs(for: [model]) + } catch { + MXLog.error("[MXCoreDataRoomSummaryStore] saveSummary couldn't obtain permanent id: \(error)") + } + } + + self.saveIfNeeded(moc) + } + } + + private func deleteSummary(forRoomId roomId: String) { + let moc = backgroundMoc + + moc.perform { [weak self] in + guard let self = self else { return } + if let existing = self.fetchSummaryMO(forRoomId: roomId, in: moc) { + moc.delete(existing) + } + + self.saveIfNeeded(moc) + } + } + + private func deleteAllSummaries() { + let entityNames: [String] = [ + MXRoomSummaryMO.entityName, + MXRoomLastMessageMO.entityName, + MXUsersTrustLevelSummaryMO.entityName, + MXRoomMembersCountMO.entityName + ] + + let moc = backgroundMoc + + moc.perform { [weak self] in + guard let self = self else { return } + do { + for entityName in entityNames { + let request = NSFetchRequest(entityName: entityName) + let deleteRequest = NSBatchDeleteRequest(fetchRequest: request) + try moc.execute(deleteRequest) + } + + self.saveIfNeeded(moc) + } catch { + MXLog.error("[MXCoreDataRoomSummaryStore] deleteAllSummaries failed: \(error)") + } + } + } + + private func allSummaries(_ completion: @escaping ([MXRoomSummaryProtocol]) -> Void) { + let request = MXRoomSummaryMO.typedFetchRequest() + + let moc = backgroundMoc + + moc.perform { + do { + let results = try moc.fetch(request) + // do not attempt to access other properties from the results + let mapped = results.compactMap({ MXRoomSummary(summaryModel: $0) }) + DispatchQueue.main.async { + completion(mapped) + } + } catch { + MXLog.error("[MXCoreDataRoomSummaryStore] fetchRoomIds failed: \(error)") + } + } + } + + /// Inline method to save a managed object context if needed. Only to be called in moc.perform blocks. + private func saveIfNeeded(_ moc: NSManagedObjectContext) { + guard moc.hasChanges else { + return + } + + var saved = false + do { + try moc.save() + saved = true + } catch { + moc.rollback() + MXLog.error("[MXCoreDataRoomSummaryStore] saveIfNeeded failed: \(error)") + } + + if saved { + // save all parent contexts recursively + if let parent = moc.parent { + parent.perform { + self.saveIfNeeded(parent) + } + } + } + } + +} + +// MARK: - MXRoomSummaryStore + +extension MXCoreDataRoomSummaryStore: MXRoomSummaryStore { + + public var rooms: [String] { + return fetchRoomIds(in: mainMoc) + } + + public var countOfRooms: UInt { + return UInt(countRooms(in: mainMoc)) + } + + public func storeSummary(_ summary: MXRoomSummaryProtocol) { + saveSummary(summary) + } + + public func summary(ofRoom roomId: String) -> MXRoomSummaryProtocol? { + return fetchSummary(forRoomId: roomId, in: mainMoc) + } + + public func removeSummary(ofRoom roomId: String) { + deleteSummary(forRoomId: roomId) + } + + public func removeAllSummaries() { + deleteAllSummaries() + } + + public func fetchAllSummaries(_ completion: @escaping ([MXRoomSummaryProtocol]) -> Void) { + allSummaries(completion) + } + +} + +// MARK: - CoreDataContextable + +extension MXCoreDataRoomSummaryStore: CoreDataContextable { + + var mainManagedObjectContext: NSManagedObjectContext { + return mainMoc + } + +} diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore.xcdatamodel/contents b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore.xcdatamodel/contents new file mode 100644 index 0000000000..2df2058d98 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/MXCoreDataRoomSummaryStore.xcdatamodeld/MXRoomSummaryCoreDataStore.xcdatamodel/contents @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomLastMessageMO.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomLastMessageMO.swift new file mode 100644 index 0000000000..051c47b113 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomLastMessageMO.swift @@ -0,0 +1,65 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +@objc(MXRoomLastMessageMO) +public class MXRoomLastMessageMO: NSManagedObject { + + internal static func typedFetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: entityName) + } + + @NSManaged public var s_eventId: String + @NSManaged public var s_originServerTs: UInt64 + @NSManaged public var s_isEncrypted: Bool + @NSManaged public var s_sender: String + @NSManaged public var s_text: String? + @NSManaged public var s_attributedText: Data? + @NSManaged public var s_others: Data? + + @discardableResult + internal static func insert(roomLastMessage lastMessage: MXRoomLastMessage, + into moc: NSManagedObjectContext) -> MXRoomLastMessageMO { + let model = MXRoomLastMessageMO(context: moc) + + model.update(withLastMessage: lastMessage) + + return model + } + + internal func update(withLastMessage lastMessage: MXRoomLastMessage) { + s_eventId = lastMessage.eventId + s_originServerTs = lastMessage.originServerTs + s_isEncrypted = lastMessage.isEncrypted + s_sender = lastMessage.sender + s_text = lastMessage.text + + if let attributedText = lastMessage.attributedText { + s_attributedText = NSKeyedArchiver.archivedData(withRootObject: attributedText) + } else { + s_attributedText = nil + } + + if let others = lastMessage.others { + s_others = NSKeyedArchiver.archivedData(withRootObject: others) + } else { + s_others = nil + } + } + +} diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomMembersCountMO.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomMembersCountMO.swift new file mode 100644 index 0000000000..b0b12005f8 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomMembersCountMO.swift @@ -0,0 +1,47 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +@objc(MXRoomMembersCountMO) +public class MXRoomMembersCountMO: NSManagedObject { + + internal static func typedFetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: entityName) + } + + @NSManaged public var s_members: Int32 + @NSManaged public var s_joined: Int32 + @NSManaged public var s_invited: Int32 + + @discardableResult + internal static func insert(roomMembersCount membersCount: MXRoomMembersCount, + into moc: NSManagedObjectContext) -> MXRoomMembersCountMO { + let model = MXRoomMembersCountMO(context: moc) + + model.update(withMembersCount: membersCount) + + return model + } + + internal func update(withMembersCount membersCount: MXRoomMembersCount) { + s_members = Int32(membersCount.members) + s_joined = Int32(membersCount.joined) + s_invited = Int32(membersCount.invited) + } + +} diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift new file mode 100644 index 0000000000..82b8d3cb4e --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXRoomSummaryMO.swift @@ -0,0 +1,306 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +internal let StringArrayDelimiter: String = ";" + +@objc(MXRoomSummaryMO) +public class MXRoomSummaryMO: NSManagedObject { + + internal static func typedFetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: entityName) + } + + @NSManaged public var s_identifier: String + @NSManaged public var s_typeString: String? + @NSManaged public var s_typeInt: Int16 + @NSManaged public var s_avatar: String? + @NSManaged public var s_displayName: String? + @NSManaged public var s_topic: String? + @NSManaged public var s_creatorUserId: String + @NSManaged public var s_aliases: String? + @NSManaged public var s_joinRule: String? + @NSManaged public var s_membershipInt: Int16 + @NSManaged public var s_membershipTransitionStateInt: Int16 + @NSManaged public var s_isConferenceUserRoom: Bool + @NSManaged public var s_others: Data? + @NSManaged public var s_isEncrypted: Bool + @NSManaged public var s_localUnreadEventCount: Int16 + @NSManaged public var s_notificationCount: Int16 + @NSManaged public var s_highlightCount: Int16 + @NSManaged public var s_hasAnyUnread: Bool + @NSManaged public var s_hasAnyNotification: Bool + @NSManaged public var s_hasAnyHighlight: Bool + @NSManaged public var s_directUserId: String? + @NSManaged public var s_hiddenFromUser: Bool + @NSManaged public var s_storedHash: Int64 + @NSManaged public var s_favoriteTagOrder: String? + @NSManaged public var s_dataTypesInt: Int64 + @NSManaged public var s_sentStatusInt: Int16 + @NSManaged public var s_parentSpaceIds: String? + @NSManaged public var s_membersCount: MXRoomMembersCountMO? + @NSManaged public var s_trust: MXUsersTrustLevelSummaryMO? + @NSManaged public var s_lastMessage: MXRoomLastMessageMO? + + @discardableResult + internal static func insert(roomSummary summary: MXRoomSummaryProtocol, + into moc: NSManagedObjectContext) -> MXRoomSummaryMO { + let model = MXRoomSummaryMO(context: moc) + + model.update(withRoomSummary: summary, in: moc) + + return model + } + + internal func update(withRoomSummary summary: MXRoomSummaryProtocol, + in moc: NSManagedObjectContext) { + s_identifier = summary.roomId + s_typeString = summary.roomTypeString + s_typeInt = Int16(summary.roomType.rawValue) + s_avatar = summary.avatar + s_displayName = summary.displayname + s_topic = summary.topic + s_creatorUserId = summary.creatorUserId + s_aliases = summary.aliases.joined(separator: StringArrayDelimiter) + s_joinRule = summary.joinRule + s_membershipInt = Int16(summary.membership.rawValue) + s_membershipTransitionStateInt = Int16(summary.membershipTransitionState.rawValue) + s_isConferenceUserRoom = summary.isConferenceUserRoom + if let others = summary.others { + s_others = NSKeyedArchiver.archivedData(withRootObject: others) + } else { + s_others = nil + } + s_isEncrypted = summary.isEncrypted + s_localUnreadEventCount = Int16(summary.localUnreadEventCount) + s_notificationCount = Int16(summary.notificationCount) + s_highlightCount = Int16(summary.highlightCount) + s_hasAnyUnread = summary.hasAnyUnread + s_hasAnyNotification = summary.hasAnyNotification + s_hasAnyHighlight = summary.hasAnyHighlight + s_directUserId = summary.directUserId + s_hiddenFromUser = summary.hiddenFromUser + s_storedHash = normalizeHash(summary.storedHash) + s_favoriteTagOrder = summary.favoriteTagOrder + s_dataTypesInt = Int64(summary.dataTypes.rawValue) + s_sentStatusInt = Int16(summary.sentStatus.rawValue) + s_parentSpaceIds = summary.parentSpaceIds.joined(separator: StringArrayDelimiter) + + let membersCountModel = s_membersCount ?? MXRoomMembersCountMO(context: moc) + membersCountModel.update(withMembersCount: summary.membersCount) + do { + try moc.obtainPermanentIDs(for: [membersCountModel]) + } catch { + MXLog.error("[MXRoomSummaryMO] update: couldn't obtain permanent id for membersCount: \(error)") + } + s_membersCount = membersCountModel + + if let trust = summary.trust { + let trustModel = s_trust ?? MXUsersTrustLevelSummaryMO(context: moc) + trustModel.update(withUsersTrustLevelSummary: trust) + do { + try moc.obtainPermanentIDs(for: [trustModel]) + } catch { + MXLog.error("[MXRoomSummaryMO] update: couldn't obtain permanent id for trust: \(error)") + } + s_trust = trustModel + } else { + if let old = s_trust { + moc.delete(old) + } + s_trust = nil + } + + if let lastMessage = summary.lastMessage { + let lastMessageModel = s_lastMessage ?? MXRoomLastMessageMO(context: moc) + lastMessageModel.update(withLastMessage: lastMessage) + do { + try moc.obtainPermanentIDs(for: [lastMessageModel]) + } catch { + MXLog.error("[MXRoomSummaryMO] update: couldn't obtain permanent id for lastMessage: \(error)") + } + s_lastMessage = lastMessageModel + } else { + if let old = s_lastMessage { + moc.delete(old) + } + s_lastMessage = nil + } + } + + private func normalizeHash(_ hash: UInt) -> Int64 { + var result = hash + while result > Int64.max { + result -= UInt(Int64.max) + } + return Int64(result) + } + +} + +// MARK: - MXRoomSummaryProtocol + +extension MXRoomSummaryMO: MXRoomSummaryProtocol { + public var roomId: String { + return s_identifier + } + + public var roomTypeString: String? { + return s_typeString + } + + public var roomType: MXRoomType { + return MXRoomType(rawValue: Int(s_typeInt)) ?? .room + } + + public var avatar: String? { + return s_avatar + } + + public var displayname: String? { + return s_displayName + } + + public var topic: String? { + return s_topic + } + + public var creatorUserId: String { + return s_creatorUserId + } + + public var aliases: [String] { + return s_aliases?.components(separatedBy: StringArrayDelimiter) ?? [] + } + + public var joinRule: String? { + return s_joinRule + } + + public var membership: MXMembership { + return MXMembership(rawValue: UInt(s_membershipInt)) ?? .unknown + } + + public var membershipTransitionState: MXMembershipTransitionState { + return MXMembershipTransitionState(rawValue: Int(s_membershipTransitionStateInt)) ?? .unknown + } + + public var membersCount: MXRoomMembersCount { + if let s_membersCount = s_membersCount { + return MXRoomMembersCount(managedObject: s_membersCount) + } + return MXRoomMembersCount() + } + + public var isConferenceUserRoom: Bool { + return s_isConferenceUserRoom + } + + public var hiddenFromUser: Bool { + return s_hiddenFromUser + } + + public var storedHash: UInt { + return UInt(s_storedHash) + } + + public var lastMessage: MXRoomLastMessage? { + if let s_lastMessage = s_lastMessage { + return MXRoomLastMessage(managedObject: s_lastMessage) + } + return nil + } + + public var isEncrypted: Bool { + return s_isEncrypted + } + + public var localUnreadEventCount: UInt { + return UInt(s_localUnreadEventCount) + } + + public var notificationCount: UInt { + return UInt(s_notificationCount) + } + + public var highlightCount: UInt { + return UInt(s_highlightCount) + } + + public var hasAnyUnread: Bool { + return s_hasAnyUnread + } + + public var hasAnyNotification: Bool { + return s_hasAnyNotification + } + + public var hasAnyHighlight: Bool { + return s_hasAnyHighlight + } + + public var isDirect: Bool { + return s_directUserId != nil + } + + public var directUserId: String? { + return s_directUserId + } + + public var others: [String : NSCoding]? { + if let s_others = s_others { + return NSKeyedUnarchiver.unarchiveObject(with: s_others) as? [String: NSCoding] + } + return nil + } + + public var favoriteTagOrder: String? { + return s_favoriteTagOrder + } + + public var dataTypes: MXRoomSummaryDataTypes { + return MXRoomSummaryDataTypes(rawValue: Int(s_dataTypesInt)) + } + + public func isTyped(_ types: MXRoomSummaryDataTypes) -> Bool { + return dataTypes.contains(types) + } + + public var sentStatus: MXRoomSummarySentStatus { + return MXRoomSummarySentStatus(rawValue: UInt(s_sentStatusInt)) ?? .ok + } + + public var spaceChildInfo: MXSpaceChildInfo? { + return nil + } + + public var parentSpaceIds: Set { + if let array = s_parentSpaceIds?.components(separatedBy: StringArrayDelimiter) { + return Set(array) + } + return [] + } + + public var trust: MXUsersTrustLevelSummary? { + if let s_trust = s_trust { + return MXUsersTrustLevelSummary(managedObject: s_trust) + } + return nil + } + +} diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXUsersTrustLevelSummaryMO.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXUsersTrustLevelSummaryMO.swift new file mode 100644 index 0000000000..e212b22d62 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/Models/MXUsersTrustLevelSummaryMO.swift @@ -0,0 +1,49 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +@objc(MXUsersTrustLevelSummaryMO) +public class MXUsersTrustLevelSummaryMO: NSManagedObject { + + internal static func typedFetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: entityName) + } + + @NSManaged public var s_usersCount: Int32 + @NSManaged public var s_trustedUsersCount: Int32 + @NSManaged public var s_devicesCount: Int32 + @NSManaged public var s_trustedDevicesCount: Int32 + + @discardableResult + internal static func insert(roomUsersTrustLevelSummary usersTrustLevelSummary: MXUsersTrustLevelSummary, + into moc: NSManagedObjectContext) -> MXUsersTrustLevelSummaryMO { + let model = MXUsersTrustLevelSummaryMO(context: moc) + + model.update(withUsersTrustLevelSummary: usersTrustLevelSummary) + + return model + } + + internal func update(withUsersTrustLevelSummary usersTrustLevelSummary: MXUsersTrustLevelSummary) { + s_usersCount = Int32(usersTrustLevelSummary.trustedUsersProgress.totalUnitCount) + s_trustedUsersCount = Int32(usersTrustLevelSummary.trustedUsersProgress.completedUnitCount) + s_devicesCount = Int32(usersTrustLevelSummary.trustedDevicesProgress.totalUnitCount) + s_trustedDevicesCount = Int32(usersTrustLevelSummary.trustedDevicesProgress.completedUnitCount) + } + +} diff --git a/MatrixSDK/Data/RoomSummaryStore/CoreData/NSManagedObject+MatrixSDK.swift b/MatrixSDK/Data/RoomSummaryStore/CoreData/NSManagedObject+MatrixSDK.swift new file mode 100644 index 0000000000..f48401d43d --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/CoreData/NSManagedObject+MatrixSDK.swift @@ -0,0 +1,30 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 Foundation +import CoreData + +extension NSManagedObject { + + /// Entity name for the managed object + static var entityName: String { + guard let result = Self.entity().name else { + fatalError("[\(String(describing: self))] could not get entity name") + } + return result + } + +} diff --git a/MatrixSDK/Data/RoomSummaryStore/File/MXFileRoomSummaryStore.h b/MatrixSDK/Data/RoomSummaryStore/File/MXFileRoomSummaryStore.h new file mode 100644 index 0000000000..ecad87edbe --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/File/MXFileRoomSummaryStore.h @@ -0,0 +1,45 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 +#import "MXMemoryRoomSummaryStore.h" +#import "MXCredentials.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + `MXFileRoomSummaryStore` extends `MXMemoryRoomSummaryStore` by adding permanent storage.. + + The files structure is the following: + + NSCachesDirectory + + MXFileRoomSummaryStore + + Matrix user id (one folder per account) + + {roomId1} + + {roomId2} + + ... + */ +@interface MXFileRoomSummaryStore : MXMemoryRoomSummaryStore + +/** + Initializer. + + @param credentials user credentials. + */ +- (instancetype)initWithCredentials:(MXCredentials *)credentials; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Data/RoomSummaryStore/File/MXFileRoomSummaryStore.m b/MatrixSDK/Data/RoomSummaryStore/File/MXFileRoomSummaryStore.m new file mode 100644 index 0000000000..fd6148a6cd --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/File/MXFileRoomSummaryStore.m @@ -0,0 +1,183 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 "MXFileRoomSummaryStore.h" +#import "MXSDKOptions.h" +#import "MXTools.h" +#import "MatrixSDKSwiftHeader.h" + +static NSString *const kMXFileRoomSummaryStoreFolder = @"MXFileRoomSummaryStore"; + +@interface MXFileRoomSummaryStore() +{ + // The user credentials + MXCredentials *credentials; + + // The path of the MXFileStore folder + NSString *storePath; + + // Execution queue for computationally expensive operations. + dispatch_queue_t executionQueue; +} +@end + +@implementation MXFileRoomSummaryStore + +- (instancetype)initWithCredentials:(MXCredentials *)credentials +{ + if (self = [super init]) + { + self->credentials = credentials; + executionQueue = dispatch_queue_create("MXFileRoomSummaryStoreExecutionQueue", DISPATCH_QUEUE_SERIAL); + [self setUpStoragePaths]; + } + return self; +} + +- (void)setUpStoragePaths +{ + // credentials must be set before this method starts execution + NSParameterAssert(credentials); + + NSString *cachePath = nil; + + NSString *applicationGroupIdentifier = [MXSDKOptions sharedInstance].applicationGroupIdentifier; + if (applicationGroupIdentifier) + { + NSURL *sharedContainerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:applicationGroupIdentifier]; + cachePath = [sharedContainerURL path]; + } + else + { + NSArray *cacheDirList = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); + cachePath = [cacheDirList objectAtIndex:0]; + } + + storePath = [[cachePath stringByAppendingPathComponent:kMXFileRoomSummaryStoreFolder] stringByAppendingPathComponent:credentials.userId]; +} + +- (void)checkStorePathExistence +{ + NSString *folder = storePath; + if (![NSFileManager.defaultManager fileExistsAtPath:folder]) + { + [[NSFileManager defaultManager] createDirectoryAtPath:folder + withIntermediateDirectories:YES + attributes:nil + error:nil]; + } +} + +- (NSString*)summaryFileForRoom:(NSString*)roomId +{ + return [storePath stringByAppendingPathComponent:roomId]; +} + +#pragma mark - MXRoomSummaryStore + +- (NSArray *)rooms +{ + NSArray *result = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:storePath error:nil]; + if (!result) + { + return @[]; + } + return result; +} + +- (NSUInteger)countOfRooms +{ + NSArray *result = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:storePath error:nil]; + return result.count; +} + +- (void)storeSummary:(id)summary +{ + [super storeSummary:summary]; + + dispatch_async(executionQueue, ^{ + NSString *file = [self summaryFileForRoom:summary.roomId]; + [self checkStorePathExistence]; + + [NSKeyedArchiver archiveRootObject:summary toFile:file]; + }); +} + +- (id)summaryOfRoom:(NSString *)roomId +{ + id summary = [super summaryOfRoom:roomId]; + if (!summary) + { + NSString *summaryFile = [self summaryFileForRoom:roomId]; + if ([[NSFileManager defaultManager] fileExistsAtPath:summaryFile]) + { + @try + { + NSDate *startDate = [NSDate date]; + summary = [NSKeyedUnarchiver unarchiveObjectWithFile:summaryFile]; + [super storeSummary:summary]; + + if ([NSThread isMainThread]) + { + MXLogWarning(@"[MXFileStore] Loaded room summary of room: %@ in %.0fms, in main thread", roomId, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); + } + } + @catch(NSException *exception) + { + MXLogError(@"[MXFileStore] Warning: room summary file for room %@ has been corrupted. Exception: %@", roomId, exception); + } + } + } + return summary; +} + +- (void)removeSummaryOfRoom:(NSString *)roomId +{ + [super removeSummaryOfRoom:roomId]; + + NSString *summaryFile = [self summaryFileForRoom:roomId]; + [[NSFileManager defaultManager] removeItemAtPath:summaryFile error:nil]; +} + +- (void)removeAllSummaries +{ + [super removeAllSummaries]; + + [[NSFileManager defaultManager] removeItemAtPath:storePath error:nil]; +} + +- (void)fetchAllSummaries:(void (^)(NSArray> * _Nonnull))completion +{ + dispatch_async(executionQueue, ^{ + NSArray *roomIDs = self.rooms; + NSMutableArray> *result = [NSMutableArray arrayWithCapacity:roomIDs.count]; + + for (NSString *roomId in roomIDs) + { + NSString *summaryFile = [self summaryFileForRoom:roomId]; + id summary = [NSKeyedUnarchiver unarchiveObjectWithFile:summaryFile]; + if (summary) + { + [result addObject:summary]; + } + } + dispatch_async(dispatch_get_main_queue(), ^{ + completion(result); + }); + }); +} + +@end diff --git a/MatrixSDK/Data/RoomSummaryStore/MXRoomSummaryStore.h b/MatrixSDK/Data/RoomSummaryStore/MXRoomSummaryStore.h index 0719f2f534..bfda43e5aa 100644 --- a/MatrixSDK/Data/RoomSummaryStore/MXRoomSummaryStore.h +++ b/MatrixSDK/Data/RoomSummaryStore/MXRoomSummaryStore.h @@ -30,22 +30,45 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) NSArray *rooms; +/** + The count of rooms, in a more efficient way. + */ +@property (nonatomic, readonly) NSUInteger countOfRooms; + /** Store the summary for a room. - @param roomId the id of the room. @param summary the room summary. */ -- (void)storeSummaryForRoom:(NSString*)roomId summary:(id)summary; +- (void)storeSummary:(id)summary; /** - Get the summary a room. + Get the summary of a room. @param roomId the id of the room. @return the user private data for this room. */ - (id _Nullable)summaryOfRoom:(NSString*)roomId; +/** + Remove the summary of a room. + + @param roomId the id of the room. + */ +- (void)removeSummaryOfRoom:(NSString*)roomId; + +/** + Remove all the room summaries. + */ +- (void)removeAllSummaries; + +/** + Fetch all summaries asynchoronously. + + @param completion completion block containing summaries, to be called at the end of the process. Should be called on main thread. + */ +- (void)fetchAllSummaries:(void(^)(NSArray>*))completion; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Data/RoomSummaryStore/Memory/MXMemoryRoomSummaryStore.h b/MatrixSDK/Data/RoomSummaryStore/Memory/MXMemoryRoomSummaryStore.h new file mode 100644 index 0000000000..cbb2689a34 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/Memory/MXMemoryRoomSummaryStore.h @@ -0,0 +1,26 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 +#import "MXRoomSummaryStore.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MXMemoryRoomSummaryStore : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Data/RoomSummaryStore/Memory/MXMemoryRoomSummaryStore.m b/MatrixSDK/Data/RoomSummaryStore/Memory/MXMemoryRoomSummaryStore.m new file mode 100644 index 0000000000..2d20882960 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/Memory/MXMemoryRoomSummaryStore.m @@ -0,0 +1,75 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 "MXMemoryRoomSummaryStore.h" + +@interface MXMemoryRoomSummaryStore() + +@property (atomic, strong) NSMutableDictionary> *cache; + +@end + +@implementation MXMemoryRoomSummaryStore + +- (instancetype)init +{ + if (self = [super init]) + { + self.cache = [NSMutableDictionary dictionary]; + } + return self; +} + +#pragma mark - MXRoomSummaryStore + +- (NSArray *)rooms +{ + return self.cache.allKeys; +} + +- (NSUInteger)countOfRooms +{ + return self.cache.count; +} + +- (void)storeSummary:(id)summary +{ + self.cache[summary.roomId] = summary; +} + +- (id)summaryOfRoom:(NSString *)roomId +{ + return self.cache[roomId]; +} + +- (void)removeSummaryOfRoom:(NSString *)roomId +{ + [self.cache removeObjectForKey:roomId]; +} + +- (void)removeAllSummaries +{ + [self.cache removeAllObjects]; +} + +- (void)fetchAllSummaries:(void (^)(NSArray> * _Nonnull))completion +{ + dispatch_async(dispatch_get_main_queue(), ^{ + completion(self.cache.allValues); + }); +} + +@end diff --git a/MatrixSDK/Data/RoomSummaryStore/Void/MXVoidRoomSummaryStore.h b/MatrixSDK/Data/RoomSummaryStore/Void/MXVoidRoomSummaryStore.h new file mode 100644 index 0000000000..c63b7e6e02 --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/Void/MXVoidRoomSummaryStore.h @@ -0,0 +1,26 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 +#import "MXRoomSummaryStore.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MXVoidRoomSummaryStore : NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Data/RoomSummaryStore/Void/MXVoidRoomSummaryStore.m b/MatrixSDK/Data/RoomSummaryStore/Void/MXVoidRoomSummaryStore.m new file mode 100644 index 0000000000..fc3c41375e --- /dev/null +++ b/MatrixSDK/Data/RoomSummaryStore/Void/MXVoidRoomSummaryStore.m @@ -0,0 +1,58 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 "MXVoidRoomSummaryStore.h" + +@implementation MXVoidRoomSummaryStore + +- (NSArray *)rooms +{ + return @[]; +} + +- (NSUInteger)countOfRooms +{ + return 0; +} + +- (void)storeSummary:(id)summary +{ + +} + +- (id)summaryOfRoom:(NSString *)roomId +{ + return nil; +} + +- (void)removeSummaryOfRoom:(NSString *)roomId +{ + +} + +- (void)removeAllSummaries +{ + +} + +- (void)fetchAllSummaries:(void (^)(NSArray> * _Nonnull))completion +{ + dispatch_async(dispatch_get_main_queue(), ^{ + completion(@[]); + }); +} + +@end diff --git a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.h b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.h index 95cb6072a6..1af18c54a4 100644 --- a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.h +++ b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.h @@ -28,20 +28,17 @@ NS_ASSUME_NONNULL_BEGIN */ typedef NS_OPTIONS(NSInteger, MXFileStorePreloadOptions) { - // Preload rooms summaries - MXFileStorePreloadOptionRoomSummary = 1 << 0, - // Preload rooms states - MXFileStorePreloadOptionRoomState = 1 << 1, + MXFileStorePreloadOptionRoomState = 1 << 0, // Preload rooms account data - MXFileStorePreloadOptionRoomAccountData = 1 << 2, + MXFileStorePreloadOptionRoomAccountData = 1 << 1, // Preload rooms messages - MXFileStorePreloadOptionRoomMessages = 1 << 3, + MXFileStorePreloadOptionRoomMessages = 1 << 2, // Preload read receipts - MXFileStorePreloadOptionReadReceipts = 1 << 4 + MXFileStorePreloadOptionReadReceipts = 1 << 3 }; /** @@ -154,15 +151,6 @@ typedef NS_OPTIONS(NSInteger, MXFileStorePreloadOptions) - (void)asyncGroups:(void (^)(NSArray *groups))success failure:(nullable void (^)(NSError *error))failure; -/** - Get the list of all stored rooms summaries. - - @param success A block object called when the operation succeeds. - @param failure A block object called when the operation fails. - */ -- (void)asyncRoomsSummaries:(void (^)(NSArray> * _Nonnull))success - failure:(nullable void (^)(NSError * _Nonnull))failure; - /** Get the stored account data for a specific room. diff --git a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m index 22d9370428..c86674855d 100644 --- a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m +++ b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m @@ -27,8 +27,9 @@ #import "MXSDKOptions.h" #import "MXTools.h" #import "MatrixSDKSwiftHeader.h" +#import "MXFileRoomSummaryStore.h" -static NSUInteger const kMXFileVersion = 77; +static NSUInteger const kMXFileVersion = 78; static NSString *const kMXFileStoreFolder = @"MXFileStore"; static NSString *const kMXFileStoreMedaDataFile = @"MXFileStore"; @@ -43,7 +44,6 @@ static NSString *const kMXFileStoreRoomMessagesFile = @"messages"; static NSString *const kMXFileStoreRoomOutgoingMessagesFile = @"outgoingMessages"; static NSString *const kMXFileStoreRoomStateFile = @"state"; -static NSString *const kMXFileStoreRoomSummaryFile = @"summary"; static NSString *const kMXFileStoreRoomAccountDataFile = @"accountData"; static NSString *const kMXFileStoreRoomReadReceiptsFile = @"readReceipts"; @@ -62,8 +62,6 @@ @interface MXFileStore () NSMutableDictionary *roomsToCommitForState; - NSMutableOrderedSet *roomsToCommitForSummary; - NSMutableDictionary *roomsToCommitForAccountData; NSMutableArray *roomsToCommitForReceipts; @@ -101,8 +99,7 @@ @interface MXFileStore () // when it will read rooms states. NSMutableDictionary *preloadedRoomsStates; - // Same kind of cache for room summary and room account data. - NSMutableDictionary> *roomSummaries; + // Same kind of cache for room account data. NSMutableDictionary *preloadedRoomAccountData; // File reading and writing operations are dispatched to a separated thread. @@ -127,13 +124,15 @@ @interface MXFileStore () @implementation MXFileStore +@synthesize roomSummaryStore; + + (void)initialize { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // By default, we do not need to preload rooms states now - preloadOptions = MXFileStorePreloadOptionRoomSummary | MXFileStorePreloadOptionRoomAccountData; + preloadOptions = MXFileStorePreloadOptionRoomAccountData; }); } @@ -145,7 +144,6 @@ - (instancetype)init; roomsToCommitForMessages = [NSMutableArray array]; roomsToCommitForOutgoingMessages = [NSMutableArray array]; roomsToCommitForState = [NSMutableDictionary dictionary]; - roomsToCommitForSummary = [NSMutableOrderedSet orderedSet]; roomsToCommitForAccountData = [NSMutableDictionary dictionary]; roomsToCommitForReceipts = [NSMutableArray array]; roomsToCommitForDeletion = [NSMutableArray array]; @@ -153,7 +151,6 @@ - (instancetype)init; groupsToCommit = [NSMutableDictionary dictionary]; groupsToCommitForDeletion = [NSMutableArray array]; preloadedRoomsStates = [NSMutableDictionary dictionary]; - roomSummaries = [NSMutableDictionary dictionary]; preloadedRoomAccountData = [NSMutableDictionary dictionary]; metaDataHasChanged = NO; @@ -171,15 +168,28 @@ - (instancetype)initWithCredentials:(MXCredentials *)someCredentials if (self) { credentials = someCredentials; + [self setupRoomSummaryStore]; [self setUpStoragePaths]; } return self; } +- (void)setupRoomSummaryStore +{ + NSParameterAssert(credentials); + + if (!roomSummaryStore) + { + roomSummaryStore = [[MXCoreDataRoomSummaryStore alloc] initWithCredentials:credentials]; + } +} + - (void)openWithCredentials:(MXCredentials*)someCredentials onComplete:(void (^)(void))onComplete failure:(void (^)(NSError *))failure { credentials = someCredentials; + [self setupRoomSummaryStore]; + // Create the file path where data will be stored for the user id passed in credentials [self setUpStoragePaths]; @@ -239,10 +249,6 @@ - (void)openWithCredentials:(MXCredentials*)someCredentials onComplete:(void (^) { [self preloadRoomsStates]; } - if (preloadOptions & MXFileStorePreloadOptionRoomSummary) - { - [self preloadRoomsSummaries]; - } if (preloadOptions & MXFileStorePreloadOptionRoomAccountData) { [self preloadRoomsAccountData]; @@ -254,7 +260,7 @@ - (void)openWithCredentials:(MXCredentials*)someCredentials onComplete:(void (^) [self loadUsers]; [self loadGroups]; - taskProfile.units = self.rooms.count; + taskProfile.units = self.roomSummaryStore.countOfRooms; [MXSDKOptions.sharedInstance.profiler stopMeasuringTaskWithProfile:taskProfile]; MXLogDebug(@"[MXFileStore] Data loaded from files in %.0fms", taskProfile.duration * 1000); } @@ -375,7 +381,7 @@ - (void)deleteRoom:(NSString *)roomId // Remove this room identifier from the other arrays. [roomsToCommitForMessages removeObject:roomId]; [roomsToCommitForState removeObjectForKey:roomId]; - [roomsToCommitForSummary removeObject:roomId]; + [roomSummaryStore removeSummaryOfRoom:roomId]; [roomsToCommitForAccountData removeObjectForKey:roomId]; [roomsToCommitForReceipts removeObject:roomId]; } @@ -405,11 +411,12 @@ - (void)deleteAllData [[NSFileManager defaultManager] createDirectoryAtPath:storeRoomsPath withIntermediateDirectories:YES attributes:nil error:nil]; [[NSFileManager defaultManager] createDirectoryAtPath:storeUsersPath withIntermediateDirectories:YES attributes:nil error:nil]; [[NSFileManager defaultManager] createDirectoryAtPath:storeGroupsPath withIntermediateDirectories:YES attributes:nil error:nil]; + + [roomSummaryStore removeAllSummaries]; // Reset data metaData = nil; [roomStores removeAllObjects]; - [roomSummaries removeAllObjects]; self.eventStreamToken = nil; } @@ -787,7 +794,6 @@ - (void)saveDataToFiles [self saveRoomsMessages]; [self saveRoomsOutgoingMessages]; [self saveRoomsState]; - [self saveRoomsSummaries]; [self saveRoomsAccountData]; [self saveReceipts]; [self saveUsers]; @@ -953,6 +959,11 @@ - (void)setUpStoragePaths // credentials must be set before this method starts execution NSParameterAssert(credentials); + if (storePath) + { + return; + } + NSString *cachePath = nil; NSString *applicationGroupIdentifier = [MXSDKOptions sharedInstance].applicationGroupIdentifier; @@ -1024,11 +1035,6 @@ - (NSString*)stateFileForRoom:(NSString*)roomId forBackup:(BOOL)backup return [[self folderForRoom:roomId forBackup:backup] stringByAppendingPathComponent:kMXFileStoreRoomStateFile]; } -- (NSString*)summaryFileForRoom:(NSString*)roomId forBackup:(BOOL)backup -{ - return [[self folderForRoom:roomId forBackup:backup] stringByAppendingPathComponent:kMXFileStoreRoomSummaryFile]; -} - - (NSString*)accountDataFileForRoom:(NSString*)roomId forBackup:(BOOL)backup { return [[self folderForRoom:roomId forBackup:backup] stringByAppendingPathComponent:kMXFileStoreRoomAccountDataFile]; @@ -1355,79 +1361,6 @@ - (void)saveRoomsState } } - -#pragma mark - Rooms summaries -/** - Preload summaries of all rooms. - - This operation must be called on the `dispatchQueue` thread to avoid blocking the main thread. - */ -- (void)preloadRoomsSummaries -{ - NSArray *roomIDs = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:storeRoomsPath error:nil]; - - NSDate *startDate = [NSDate date]; - - for (NSString *roomId in roomIDs) - { - id summary = [self summaryOfRoom:roomId]; - if (summary) - { - @synchronized (roomSummaries) { - roomSummaries[roomId] = summary; - } - } - } - - MXLogDebug(@"[MXFileStore] Loaded rooms summaries data of %tu rooms in %.0fms", roomIDs.count, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); -} - -- (void)saveRoomsSummaries -{ - if (roomsToCommitForSummary.count) - { - // Take a snapshot of room ids to store to process them on the other thread - NSArray *roomsToCommit = [NSArray arrayWithArray:roomsToCommitForSummary.array]; - [roomsToCommitForSummary removeAllObjects]; -#if DEBUG - MXLogDebug(@"[MXFileStore commit] queuing saveRoomsSummaries for %tu rooms", roomsToCommit.count); -#endif - MXWeakify(self); - dispatch_async(dispatchQueue, ^(void){ - MXStrongifyAndReturnIfNil(self); -#if DEBUG - NSDate *startDate = [NSDate date]; -#endif - for (NSString *roomId in roomsToCommit) - { - MXRoomSummary *summary; - @synchronized (self->roomSummaries) - { - summary = (MXRoomSummary *)self->roomSummaries[roomId]; - } - - NSString *file = [self summaryFileForRoom:roomId forBackup:NO]; - NSString *backupFile = [self summaryFileForRoom:roomId forBackup:YES]; - - // Backup the file - if (backupFile && [[NSFileManager defaultManager] fileExistsAtPath:file]) - { - [self checkFolderExistenceForRoom:roomId forBackup:YES]; - [[NSFileManager defaultManager] moveItemAtPath:file toPath:backupFile error:nil]; - } - - // Store new data - [self checkFolderExistenceForRoom:roomId forBackup:NO]; - [NSKeyedArchiver archiveRootObject:summary toFile:file]; - } -#if DEBUG - MXLogDebug(@"[MXFileStore commit] lasted %.0fms for summaries for %tu rooms", [[NSDate date] timeIntervalSinceDate:startDate] * 1000, roomsToCommit.count); -#endif - }); - } -} - - #pragma mark - Rooms account data /** Preload account data of all rooms. @@ -2176,25 +2109,6 @@ - (void)asyncGroups:(void (^)(NSArray *groups))success failure:(nulla }); } -- (void)asyncRoomsSummaries:(void (^)(NSArray> * _Nonnull))success - failure:(nullable void (^)(NSError * _Nonnull))failure -{ - MXWeakify(self); - dispatch_async(dispatchQueue, ^{ - MXStrongifyAndReturnIfNil(self); - - [self preloadRoomsSummaries]; - - MXWeakify(self); - dispatch_async(dispatch_get_main_queue(), ^{ - MXStrongifyAndReturnIfNil(self); - @synchronized (self->roomSummaries) { - success(self->roomSummaries.allValues); - } - }); - }); -} - - (void)asyncAccountDataOfRoom:(NSString *)roomId success:(void (^)(MXRoomAccountData * _Nonnull))success failure:(nullable void (^)(NSError * _Nonnull))failure { dispatch_async(dispatchQueue, ^{ @@ -2283,58 +2197,4 @@ - (void)loadRoomMessagesForRoom:(NSString *)roomId completion:(void (^)(void))co }); } -#pragma mark - MXRoomSummaryStore - -- (NSArray *)rooms -{ - NSMutableArray *roomIDs = [NSMutableArray arrayWithArray:[[NSFileManager defaultManager] contentsOfDirectoryAtPath:storeRoomsPath error:nil]]; - [roomIDs removeObjectsInArray:roomsToCommitForDeletion]; - return roomIDs; -} - -- (void)storeSummaryForRoom:(NSString *)roomId summary:(id)summary -{ - @synchronized (roomSummaries) - { - roomSummaries[roomId] = summary; - } - - [roomsToCommitForSummary addObject:roomId]; -} - -- (id)summaryOfRoom:(NSString *)roomId -{ - id summary; - @synchronized (roomSummaries) - { - // First, try to get the data from the cache - summary = roomSummaries[roomId]; - } - if (!summary) - { - NSString *summaryFile = [self summaryFileForRoom:roomId forBackup:NO]; - if ([[NSFileManager defaultManager] fileExistsAtPath:summaryFile]) - { - @try - { - NSDate *startDate = [NSDate date]; - summary = [NSKeyedUnarchiver unarchiveObjectWithFile:summaryFile]; - @synchronized (roomSummaries) - { - roomSummaries[roomId] = summary; - } - if ([NSThread isMainThread]) - { - MXLogWarning(@"[MXFileStore] Loaded room summary of room: %@ in %.0fms, in main thread", roomId, [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - } - } - @catch(NSException *exception) - { - MXLogError(@"[MXFileStore] Warning: room summary file for room %@ has been corrupted. Exception: %@", roomId, exception); - } - } - } - return summary; -} - @end diff --git a/MatrixSDK/Data/Store/MXMemoryStore/MXMemoryStore.m b/MatrixSDK/Data/Store/MXMemoryStore/MXMemoryStore.m index 84a3d93f6c..24404c9c41 100644 --- a/MatrixSDK/Data/Store/MXMemoryStore/MXMemoryStore.m +++ b/MatrixSDK/Data/Store/MXMemoryStore/MXMemoryStore.m @@ -20,6 +20,7 @@ #import "MXMemoryRoomStore.h" #import "MXTools.h" +#import "MXMemoryRoomSummaryStore.h" @interface MXMemoryStore() { @@ -29,13 +30,14 @@ @interface MXMemoryStore() // Execution queue for computationally expensive operations. dispatch_queue_t executionQueue; - NSMutableDictionary> *roomSummaries; } @end @implementation MXMemoryStore +@synthesize roomSummaryStore; + @synthesize storeService, eventStreamToken, userAccountData, syncFilterId, homeserverWellknown, areAllIdentityServerTermsAgreed; - (instancetype)init @@ -48,7 +50,7 @@ - (instancetype)init roomReceiptsStores = [NSMutableDictionary dictionary]; users = [NSMutableDictionary dictionary]; groups = [NSMutableDictionary dictionary]; - roomSummaries = [NSMutableDictionary dictionary]; + roomSummaryStore = [[MXMemoryRoomSummaryStore alloc] init]; maxUploadSize = -1; areAllIdentityServerTermsAgreed = NO; executionQueue = dispatch_queue_create("MXMemoryStoreExecutionQueue", DISPATCH_QUEUE_SERIAL); @@ -108,11 +110,14 @@ - (void)deleteRoom:(NSString *)roomId { [roomReceiptsStores removeObjectForKey:roomId]; } + + [roomSummaryStore removeSummaryOfRoom:roomId]; } - (void)deleteAllData { [roomStores removeAllObjects]; + [roomSummaryStore removeAllSummaries]; } - (void)storePaginationTokenOfRoom:(NSString*)roomId andToken:(NSString*)token @@ -317,7 +322,6 @@ - (BOOL)isPermanent return NO; } - #pragma mark - Matrix users - (void)storeUser:(MXUser *)user { @@ -484,25 +488,4 @@ - (RoomReceiptsStore *)getOrCreateRoomReceiptsStore:(NSString *)roomId return store; } -#pragma mark - MXRoomSummaryStore - -- (NSArray *)rooms -{ - return roomStores.allKeys; -} - -- (void)storeSummaryForRoom:(NSString *)roomId summary:(id)summary -{ - roomSummaries[roomId] = summary; - if (roomStores[roomId] == nil) - { - roomStores[roomId] = [[MXMemoryRoomStore alloc] init]; - } -} - -- (id)summaryOfRoom:(NSString *)roomId -{ - return roomSummaries[roomId]; -} - @end diff --git a/MatrixSDK/Data/Store/MXNoStore/MXNoStore.m b/MatrixSDK/Data/Store/MXNoStore/MXNoStore.m index a5c6a9ab18..a350862627 100644 --- a/MatrixSDK/Data/Store/MXNoStore/MXNoStore.m +++ b/MatrixSDK/Data/Store/MXNoStore/MXNoStore.m @@ -18,6 +18,7 @@ #import "MXNoStore.h" #import "MXEventsEnumeratorOnArray.h" +#import "MXVoidRoomSummaryStore.h" @interface MXNoStore () { @@ -55,6 +56,8 @@ @interface MXNoStore () @implementation MXNoStore +@synthesize roomSummaryStore; + @synthesize eventStreamToken, userAccountData, syncFilterId; - (instancetype)init @@ -71,6 +74,7 @@ - (instancetype)init partialTextMessages = [NSMutableDictionary dictionary]; users = [NSMutableDictionary dictionary]; groups = [NSMutableDictionary dictionary]; + roomSummaryStore = [[MXVoidRoomSummaryStore alloc] init]; } return self; } @@ -164,6 +168,7 @@ - (void)deleteRoom:(NSString *)roomId { [partialTextMessages removeObjectForKey:roomId]; } + [roomSummaryStore removeSummaryOfRoom:roomId]; } - (void)deleteAllData @@ -175,6 +180,7 @@ - (void)deleteAllData [hasLoadedAllRoomMembersForRooms removeAllObjects]; [lastMessages removeAllObjects]; [partialTextMessages removeAllObjects]; + [roomSummaryStore removeAllSummaries]; } - (void)storePaginationTokenOfRoom:(NSString*)roomId andToken:(NSString*)token @@ -435,21 +441,4 @@ - (void)close [groups removeAllObjects]; } -#pragma mark - MXRoomSummaryStore - -- (NSArray *)rooms -{ - return @[]; -} - -- (void)storeSummaryForRoom:(NSString *)roomId summary:(id)summary -{ - -} - -- (id)summaryOfRoom:(NSString *)roomId -{ - return nil; -} - @end diff --git a/MatrixSDK/Data/Store/MXStore.h b/MatrixSDK/Data/Store/MXStore.h index fe62f22999..7200c04933 100644 --- a/MatrixSDK/Data/Store/MXStore.h +++ b/MatrixSDK/Data/Store/MXStore.h @@ -37,7 +37,9 @@ The `MXStore` protocol defines an interface that must be implemented in order to store Matrix data handled during a `MXSession`. */ -@protocol MXStore +@protocol MXStore + +@property (nonatomic, readonly) id _Nonnull roomSummaryStore; #pragma mark - Store Management diff --git a/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.h b/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.h index de8fbfcee8..9ba7706e80 100644 --- a/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.h +++ b/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.h @@ -20,8 +20,16 @@ NS_ASSUME_NONNULL_BEGIN +typedef NS_ENUM(NSUInteger, MXEventAssetType) +{ + MXEventAssetTypeUser, + MXEventAssetTypeGeneric +}; + @interface MXEventContentLocation: MXJSONModel +@property (nonatomic, readonly) MXEventAssetType assetType; + @property (nonatomic, readonly) double latitude; @property (nonatomic, readonly) double longitude; @@ -30,9 +38,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, nullable) NSString *locationDescription; -- (instancetype)initWithLatitude:(double)latitude - longitude:(double)longitude - description:(nullable NSString *)description; +- (instancetype)initWithAssetType:(MXEventAssetType)assetType + latitude:(double)latitude + longitude:(double)longitude + description:(nullable NSString *)description; @end diff --git a/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.m b/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.m index d210c41c06..431d2007c0 100644 --- a/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.m +++ b/MatrixSDK/JSONModels/Event/Content/MXEventContentLocation.m @@ -19,12 +19,14 @@ @implementation MXEventContentLocation -- (instancetype)initWithLatitude:(double)latitude - longitude:(double)longitude - description:(NSString *)description +- (instancetype)initWithAssetType:(MXEventAssetType)assetType + latitude:(double)latitude + longitude:(double)longitude + description:(NSString *)description { if (self = [super init]) { + _assetType = assetType; _latitude = latitude; _longitude = longitude; _locationDescription = description; @@ -36,45 +38,73 @@ - (instancetype)initWithLatitude:(double)latitude + (instancetype)modelFromJSON:(NSDictionary *)JSONDictionary { - NSDictionary *locationContent = JSONDictionary[kMXMessageContentKeyExtensibleLocationMSC3488]; - if (locationContent == nil) + NSString *description; + NSString *geoURIString; + MXEventAssetType assetType = MXEventAssetTypeUser; + + NSDictionary *locationDictionary = JSONDictionary[kMXMessageContentKeyExtensibleLocationMSC3488]; + if (locationDictionary == nil) { - locationContent = JSONDictionary[kMXMessageContentKeyExtensibleLocation]; + locationDictionary = JSONDictionary[kMXMessageContentKeyExtensibleLocation]; } - if (locationContent == nil) { + if (locationDictionary) + { + MXJSONModelSetString(geoURIString, locationDictionary[kMXMessageContentKeyExtensibleLocationURI]); + MXJSONModelSetString(description, locationDictionary[kMXMessageContentKeyExtensibleLocationDescription]); + } + else if ([JSONDictionary[kMXMessageTypeKey] isEqualToString:kMXMessageTypeLocation]) + { + MXJSONModelSetString(geoURIString, JSONDictionary[kMXMessageGeoURIKey]); + } + else + { return nil; } - NSString *description; - MXJSONModelSetString(description, locationContent[kMXMessageContentKeyExtensibleLocationDescription]); + NSDictionary *assetDictionary = JSONDictionary[kMXMessageContentKeyExtensibleAssetMSC3488]; + if (assetDictionary == nil) + { + assetDictionary = JSONDictionary[kMXMessageContentKeyExtensibleAsset]; + } - NSString *geoURIString; - MXJSONModelSetString(geoURIString, locationContent[kMXMessageContentKeyExtensibleLocationURI]); + if (assetDictionary) + { + if (![assetDictionary[kMXMessageContentKeyExtensibleAssetType] isEqualToString:kMXMessageContentKeyExtensibleAssetTypeUser]) { + assetType = MXEventAssetTypeGeneric; + } + } NSString *locationString = [[geoURIString componentsSeparatedByString:@":"].lastObject componentsSeparatedByString:@";"].firstObject; - NSArray *locationComponents = [locationString componentsSeparatedByString:@","]; - if (locationComponents.count != 2) { + if (locationComponents.count != 2) + { return nil; } double latitude = [locationComponents.firstObject doubleValue]; double longitude = [locationComponents.lastObject doubleValue]; - return [[MXEventContentLocation alloc] initWithLatitude:latitude - longitude:longitude - description:description]; + return [[MXEventContentLocation alloc] initWithAssetType:assetType + latitude:latitude + longitude:longitude + description:description]; } - (NSDictionary *)JSONDictionary { NSMutableDictionary *content = [NSMutableDictionary dictionary]; - content[kMXMessageContentKeyExtensibleLocationURI] = self.geoURI; + NSMutableDictionary *locationContent = [NSMutableDictionary dictionary]; + locationContent[kMXMessageContentKeyExtensibleLocationURI] = self.geoURI; + locationContent[kMXMessageContentKeyExtensibleLocationDescription] = self.locationDescription; + content[kMXMessageContentKeyExtensibleLocationMSC3488] = locationContent; + + content[kMXMessageContentKeyExtensibleAssetMSC3488] = @{ kMXMessageContentKeyExtensibleAssetType: kMXMessageContentKeyExtensibleAssetTypeUser }; - content[kMXMessageContentKeyExtensibleLocationDescription] = self.locationDescription; + content[kMXMessageTypeKey] = kMXMessageTypeLocation; + content[kMXMessageGeoURIKey] = self.geoURI; return content; } diff --git a/MatrixSDK/JSONModels/Event/Content/MXEventContentPollStart.m b/MatrixSDK/JSONModels/Event/Content/MXEventContentPollStart.m index dbd2739efe..79e3a00836 100644 --- a/MatrixSDK/JSONModels/Event/Content/MXEventContentPollStart.m +++ b/MatrixSDK/JSONModels/Event/Content/MXEventContentPollStart.m @@ -36,10 +36,25 @@ - (instancetype)initWithQuestion:(NSString *)question + (instancetype)modelFromJSON:(NSDictionary *)JSONDictionary { - NSDictionary *content = JSONDictionary[kMXMessageContentKeyExtensiblePollStart]; + NSDictionary *content = JSONDictionary[kMXMessageContentKeyExtensiblePollStartMSC3381]; + if (content == nil) { + content = JSONDictionary[kMXMessageContentKeyExtensiblePollStart]; + } + + if (content == nil) { + return nil; + } - NSString *question, *kind; - MXJSONModelSetString(question, content[kMXMessageContentKeyExtensiblePollQuestion][kMXMessageContentKeyExtensibleText]); + NSString *question = content[kMXMessageContentKeyExtensiblePollQuestion][kMXMessageContentKeyExtensibleTextMSC1767]; + if (question == nil) { + question = content[kMXMessageContentKeyExtensiblePollQuestion][kMXMessageContentKeyExtensibleText]; + } + + if (question.length == 0) { + return nil; + } + + NSString *kind; MXJSONModelSetString(kind, content[kMXMessageContentKeyExtensiblePollKind]); NSNumber *maxSelections; @@ -57,7 +72,7 @@ - (NSDictionary *)JSONDictionary { NSMutableDictionary *content = [NSMutableDictionary dictionary]; - content[kMXMessageContentKeyExtensiblePollQuestion] = @{kMXMessageContentKeyExtensibleText: self.question}; + content[kMXMessageContentKeyExtensiblePollQuestion] = @{kMXMessageContentKeyExtensibleTextMSC1767: self.question}; content[kMXMessageContentKeyExtensiblePollKind] = self.kind; content[kMXMessageContentKeyExtensiblePollMaxSelections] = self.maxSelections; @@ -69,7 +84,7 @@ - (NSDictionary *)JSONDictionary content[kMXMessageContentKeyExtensiblePollAnswers] = answerOptions; - return @{kMXMessageContentKeyExtensiblePollStart: content}; + return @{kMXMessageContentKeyExtensiblePollStartMSC3381: content}; } @end @@ -90,9 +105,17 @@ - (instancetype)initWithUUID:(NSString *)uuid text:(NSString *)text + (instancetype)modelFromJSON:(NSDictionary *)JSONDictionary { - NSString *uuid, *text; + NSString *uuid; MXJSONModelSetString(uuid, JSONDictionary[kMXMessageContentKeyExtensiblePollAnswerId]); - MXJSONModelSetString(text, JSONDictionary[kMXMessageContentKeyExtensibleText]); + + NSString *text = JSONDictionary[kMXMessageContentKeyExtensibleTextMSC1767]; + if (text == nil) { + text = JSONDictionary[kMXMessageContentKeyExtensibleText]; + } + + if (text.length == 0) { + return nil; + } return [[MXEventContentPollStartAnswerOption alloc] initWithUUID:uuid text:text]; } @@ -102,7 +125,7 @@ - (NSDictionary *)JSONDictionary NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; JSONDictionary[kMXMessageContentKeyExtensiblePollAnswerId] = self.uuid; - JSONDictionary[kMXMessageContentKeyExtensibleText] = self.text; + JSONDictionary[kMXMessageContentKeyExtensibleTextMSC1767] = self.text; return JSONDictionary; } diff --git a/MatrixSDK/JSONModels/MXEvent.h b/MatrixSDK/JSONModels/MXEvent.h index 28368129b3..1421924f54 100644 --- a/MatrixSDK/JSONModels/MXEvent.h +++ b/MatrixSDK/JSONModels/MXEvent.h @@ -211,6 +211,7 @@ FOUNDATION_EXPORT NSString *const kMXEventRelationRelatesToKey; FOUNDATION_EXPORT NSString *const MXEventRelationTypeAnnotation; // Reactions FOUNDATION_EXPORT NSString *const MXEventRelationTypeReference; // Reply FOUNDATION_EXPORT NSString *const MXEventRelationTypeReplace; // Edition +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyNewContent; // Edited content key FOUNDATION_EXPORT NSString *const MXEventRelationTypeThread; // Thread /** @@ -222,15 +223,20 @@ FOUNDATION_EXPORT NSString *const kMXEventLocalEventIdPrefix; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyVoiceMessage; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyVoiceMessageMSC2516; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyVoiceMessageMSC3245; + FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAudio; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAudioMSC1767; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAudioDuration; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAudioWaveform; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleText; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleTextMSC1767; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleTimestamp; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleTimestampMSC3488; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleFile; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleFileMSC1767; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleFileSize; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleFileName; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleFileURL; @@ -238,8 +244,14 @@ FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleFileMimeType; // Polls FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollStart; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollStartMSC3381; + FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollResponse; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollResponseMSC3381; + FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollEnd; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollEndMSC3381; + FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollQuestion; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollAnswers; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensiblePollAnswerId; @@ -254,6 +266,13 @@ FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleLocationMSC3488; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleLocationURI; FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleLocationDescription; +// Assets + +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAsset; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAssetMSC3488; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAssetType; +FOUNDATION_EXPORT NSString *const kMXMessageContentKeyExtensibleAssetTypeUser; + /** The internal event state used to handle the different steps of the event sending. */ diff --git a/MatrixSDK/JSONModels/MXEvent.m b/MatrixSDK/JSONModels/MXEvent.m index f1d2f95bf0..fb42b8dc71 100644 --- a/MatrixSDK/JSONModels/MXEvent.m +++ b/MatrixSDK/JSONModels/MXEvent.m @@ -87,12 +87,12 @@ NSString *const kMXEventTypeStringAutoJoinKey = @"auto_join"; NSString *const kMXEventTypeStringSuggestedKey = @"suggested"; -NSString *const kMXEventTypeStringPollStartMSC3381 = @"org.matrix.msc3381.poll.start"; NSString *const kMXEventTypeStringPollStart = @"m.poll.start"; -NSString *const kMXEventTypeStringPollResponseMSC3381 = @"org.matrix.msc3381.poll.response"; +NSString *const kMXEventTypeStringPollStartMSC3381 = @"org.matrix.msc3381.poll.start"; NSString *const kMXEventTypeStringPollResponse = @"m.poll.response"; -NSString *const kMXEventTypeStringPollEndMSC3381 = @"org.matrix.msc3381.poll.end"; +NSString *const kMXEventTypeStringPollResponseMSC3381 = @"org.matrix.msc3381.poll.response"; NSString *const kMXEventTypeStringPollEnd = @"m.poll.end"; +NSString *const kMXEventTypeStringPollEndMSC3381 = @"org.matrix.msc3381.poll.end"; NSString *const kMXMessageTypeKey = @"msgtype"; NSString *const kMXMessageTypeText = @"m.text"; @@ -113,6 +113,7 @@ NSString *const MXEventRelationTypeAnnotation = @"m.annotation"; NSString *const MXEventRelationTypeReference = @"m.reference"; NSString *const MXEventRelationTypeReplace = @"m.replace"; +NSString *const kMXMessageContentKeyNewContent = @"m.new_content"; // TODO: Replace when the MSC merged // https://github.com/matrix-org/matrix-doc/pull/3440 NSString *const MXEventRelationTypeThread = @"io.element.thread"; @@ -127,29 +128,39 @@ NSString *const kMXEventIdentifierKey = @"kMXEventIdentifierKey"; -NSString *const kMXMessageContentKeyVoiceMessageMSC2516 = @"org.matrix.msc2516.voice"; -NSString *const kMXMessageContentKeyVoiceMessageMSC3245 = @"org.matrix.msc3245.voice"; -NSString *const kMXMessageContentKeyVoiceMessage = @"m.voice"; +NSString *const kMXMessageContentKeyVoiceMessage = @"m.voice"; +NSString *const kMXMessageContentKeyVoiceMessageMSC2516 = @"org.matrix.msc2516.voice"; +NSString *const kMXMessageContentKeyVoiceMessageMSC3245 = @"org.matrix.msc3245.voice"; -NSString *const kMXMessageContentKeyExtensibleAudio = @"org.matrix.msc1767.audio"; +NSString *const kMXMessageContentKeyExtensibleAudio = @"m.audio"; +NSString *const kMXMessageContentKeyExtensibleAudioMSC1767 = @"org.matrix.msc1767.audio"; NSString *const kMXMessageContentKeyExtensibleAudioDuration = @"duration"; NSString *const kMXMessageContentKeyExtensibleAudioWaveform = @"waveform"; -NSString *const kMXMessageContentKeyExtensibleText = @"org.matrix.msc1767.text"; +NSString *const kMXMessageContentKeyExtensibleText = @"m.text"; +NSString *const kMXMessageContentKeyExtensibleTextMSC1767 = @"org.matrix.msc1767.text"; -NSString *const kMXMessageContentKeyExtensibleTimestamp = @"org.matrix.msc3488.ts"; +NSString *const kMXMessageContentKeyExtensibleTimestamp = @"m.ts"; +NSString *const kMXMessageContentKeyExtensibleTimestampMSC3488 = @"org.matrix.msc3488.ts"; -NSString *const kMXMessageContentKeyExtensibleFile = @"org.matrix.msc1767.file"; -NSString *const kMXMessageContentKeyExtensibleFileSize = @"size"; -NSString *const kMXMessageContentKeyExtensibleFileName = @"name"; -NSString *const kMXMessageContentKeyExtensibleFileURL = @"url"; -NSString *const kMXMessageContentKeyExtensibleFileMimeType = @"mimetype"; +NSString *const kMXMessageContentKeyExtensibleFile = @"m.file"; +NSString *const kMXMessageContentKeyExtensibleFileMSC1767 = @"org.matrix.msc1767.file"; +NSString *const kMXMessageContentKeyExtensibleFileSize = @"size"; +NSString *const kMXMessageContentKeyExtensibleFileName = @"name"; +NSString *const kMXMessageContentKeyExtensibleFileURL = @"url"; +NSString *const kMXMessageContentKeyExtensibleFileMimeType = @"mimetype"; // Polls -NSString *const kMXMessageContentKeyExtensiblePollStart = @"org.matrix.msc3381.poll.start"; -NSString *const kMXMessageContentKeyExtensiblePollResponse = @"org.matrix.msc3381.poll.response"; -NSString *const kMXMessageContentKeyExtensiblePollEnd = @"org.matrix.msc3381.poll.end"; +NSString *const kMXMessageContentKeyExtensiblePollStart = @"m.poll.start"; +NSString *const kMXMessageContentKeyExtensiblePollStartMSC3381 = @"org.matrix.msc3381.poll.start"; + +NSString *const kMXMessageContentKeyExtensiblePollResponse = @"m.poll.response"; +NSString *const kMXMessageContentKeyExtensiblePollResponseMSC3381 = @"org.matrix.msc3381.poll.response"; + +NSString *const kMXMessageContentKeyExtensiblePollEnd = @"m.poll.end"; +NSString *const kMXMessageContentKeyExtensiblePollEndMSC3381 = @"org.matrix.msc3381.poll.end"; + NSString *const kMXMessageContentKeyExtensiblePollQuestion = @"question"; NSString *const kMXMessageContentKeyExtensiblePollAnswers = @"answers"; NSString *const kMXMessageContentKeyExtensiblePollAnswerId = @"id"; @@ -165,6 +176,13 @@ NSString *const kMXMessageContentKeyExtensibleLocationURI = @"uri"; NSString *const kMXMessageContentKeyExtensibleLocationDescription = @"description"; +// Assets + +NSString *const kMXMessageContentKeyExtensibleAsset = @"m.asset"; +NSString *const kMXMessageContentKeyExtensibleAssetMSC3488 = @"org.matrix.msc3488.asset"; +NSString *const kMXMessageContentKeyExtensibleAssetType = @"type"; +NSString *const kMXMessageContentKeyExtensibleAssetTypeUser = @"m.self"; + #pragma mark - MXEvent @interface MXEvent () { @@ -476,7 +494,7 @@ - (BOOL)isMediaAttachment - (BOOL)isEditEvent { - return self.eventType == MXEventTypeRoomMessage && [self.relatesTo.relationType isEqualToString:MXEventRelationTypeReplace]; + return [self.relatesTo.relationType isEqualToString:MXEventRelationTypeReplace]; } - (BOOL)isReplyEvent @@ -668,20 +686,22 @@ - (MXEvent*)editedEventFromReplacementEvent:(MXEvent*)replaceEvent // Reuse its decryption data replaceEventDecryptionResult = [replaceEvent decryptionResult]; } - else if (event.content[kMXMessageBodyKey] && newContentDict && [newContentDict[kMXMessageTypeKey] isEqualToString:event.content[kMXMessageTypeKey]]) + else if (newContentDict) { editedEventDict = [event.JSONDictionary mutableCopy]; NSMutableDictionary *editedEventContentDict = [editedEventDict[@"content"] mutableCopy]; editedEventContentDict[kMXMessageBodyKey] = newContentDict[kMXMessageBodyKey]; editedEventContentDict[@"formatted_body"] = newContentDict[@"formatted_body"]; editedEventContentDict[@"format"] = newContentDict[@"format"]; + editedEventContentDict[kMXEventTypeStringPollStart] = newContentDict[kMXEventTypeStringPollStart]; + editedEventContentDict[kMXEventTypeStringPollStartMSC3381] = newContentDict[kMXEventTypeStringPollStartMSC3381]; editedEventDict[@"content"] = editedEventContentDict; } if (editedEventDict) { // Use the same type as the replace event - // This is useful for local echoes in e2e room as local echoes are always non encryted/ + // This is useful for local echoes in e2e room as local echoes are always non encrypted/ // So, there are switching between "m.room.encrypted" and "m.room.message" editedEventDict[@"type"] = replaceEvent.isEncrypted ? @"m.room.encrypted" : replaceEvent.type; diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index f074f2757a..cf1c83fc86 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -3437,7 +3437,7 @@ - (MXHTTPOperation *)syncFromToken:(NSString*)token MXJSONModelSetDictionary(join, rooms[@"join"]); MXJSONModelSetDictionary(invite, rooms[@"invite"]); MXJSONModelSetDictionary(leave, rooms[@"leave"]); - initialSyncRequestTaskProfile.units = join.count + invite.count + leave.count; + initialSyncRequestTaskProfile.units = join.count; [profiler stopMeasuringTaskWithProfile:initialSyncRequestTaskProfile]; } @@ -3458,7 +3458,7 @@ - (MXHTTPOperation *)syncFromToken:(NSString*)token if (initialSyncParsingTaskProfile) { // Contextualise the profiling with the amount of received information - initialSyncParsingTaskProfile.units = syncResponse.rooms.join.count + syncResponse.rooms.invite.count + syncResponse.rooms.leave.count; + initialSyncParsingTaskProfile.units = syncResponse.rooms.join.count; [profiler stopMeasuringTaskWithProfile:initialSyncParsingTaskProfile]; } diff --git a/MatrixSDK/MXSDKOptions.h b/MatrixSDK/MXSDKOptions.h index 73ac64ed66..610672080a 100644 --- a/MatrixSDK/MXSDKOptions.h +++ b/MatrixSDK/MXSDKOptions.h @@ -170,8 +170,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) MXCallTransferType callTransferType; /** - The class of room list data manager. This class must be conformed to MXRoomListDataManager protocol. - By default this class is MXStoreRoomListDataManager. + The class of room list data manager. This class must conform to MXRoomListDataManager protocol. + By default this class is MXCoreDataRoomListDataManager. */ @property (nonatomic) Class roomListDataManagerClass; diff --git a/MatrixSDK/MXSDKOptions.m b/MatrixSDK/MXSDKOptions.m index 17bcaae778..c975c5ecb8 100644 --- a/MatrixSDK/MXSDKOptions.m +++ b/MatrixSDK/MXSDKOptions.m @@ -49,7 +49,7 @@ - (instancetype)init _HTTPAdditionalHeaders = @{}; _autoAcceptRoomInvites = NO; _callTransferType = MXCallTransferTypeBridged; - self.roomListDataManagerClass = [MXStoreRoomListDataManager class]; + self.roomListDataManagerClass = [MXCoreDataRoomListDataManager class]; _clientPermalinkBaseUrl = nil; } diff --git a/MatrixSDK/MXSession.h b/MatrixSDK/MXSession.h index a6ba2fe566..19ed192c32 100644 --- a/MatrixSDK/MXSession.h +++ b/MatrixSDK/MXSession.h @@ -1057,13 +1057,6 @@ typedef void (^MXOnBackgroundSyncFail)(NSError *error); */ - (MXRoomSummary *)roomSummaryWithRoomId:(NSString*)roomId; -/** - Get the list of all rooms summaries. - - @return an array of MXRoomSummary. - */ -- (NSArray*)roomsSummaries; - /** Recompute all room summaries last message. diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m index cbb5f94d7c..b2f43e12cf 100644 --- a/MatrixSDK/MXSession.m +++ b/MatrixSDK/MXSession.m @@ -108,7 +108,7 @@ @interface MXSession () Rooms summaries Each key is a room id. Each value, the MXRoomSummary instance. */ - NSMutableDictionary *roomsSummaries; + NSMutableDictionary *roomSummaries; /** The current request of the event stream. @@ -224,7 +224,7 @@ - (id)initWithMatrixRestClient:(MXRestClient*)mxRestClient _threePidAddManager = [[MX3PidAddManager alloc] initWithMatrixSession:self]; mediaManager = [[MXMediaManager alloc] initWithHomeServer:matrixRestClient.homeserver]; rooms = [NSMutableDictionary dictionary]; - roomsSummaries = [NSMutableDictionary dictionary]; + roomSummaries = [NSMutableDictionary dictionary]; _roomSummaryUpdateDelegate = [MXRoomSummaryUpdater roomSummaryUpdaterForSession:self]; _roomAccountDataUpdateDelegate = [MXRoomAccountDataUpdater roomAccountDataUpdaterForSession:self]; globalEventListeners = [NSMutableArray array]; @@ -405,23 +405,13 @@ -(void)setStore:(id)store success:(void (^)(void))onStoreDataReady fail // Load MXRoomSummaries from the store NSDate *startDate2 = [NSDate date]; - for (NSString *roomId in self.store.rooms) - { - @autoreleasepool - { - MXRoomSummary *summary = [[MXRoomSummary alloc] initWithRoomId:roomId andMatrixSession:self]; - if (summary) - { - self->roomsSummaries[roomId] = summary; - } - } - } - - MXLogDebug(@"[MXSession] Built %lu MXRoomSummaries in %.0fms", (unsigned long)self->roomsSummaries.allKeys.count, [[NSDate date] timeIntervalSinceDate:startDate2] * 1000); + NSArray *roomIds = self.store.roomSummaryStore.rooms; + + MXLogDebug(@"[MXSession] Read %lu room ids in %.0fms", (unsigned long)roomIds.count, [[NSDate date] timeIntervalSinceDate:startDate2] * 1000); // Create MXRooms from their states stored in the store NSDate *startDate3 = [NSDate date]; - for (NSString *roomId in self.store.rooms) + for (NSString *roomId in roomIds) { [self loadRoom:roomId]; } @@ -848,7 +838,7 @@ - (void)_startWithSyncFilterId:(NSString *)syncFilterId onServerSyncDone:(void ( [self setState:MXSessionStateSyncInProgress]; // Can we resume from data available in the cache - if (self.store.isPermanent && self.isEventStreamInitialised && 0 < self.store.rooms.count) + if (self.store.isPermanent && self.isEventStreamInitialised && 0 < self.store.roomSummaryStore.countOfRooms) { // Resume the stream (presence will be retrieved during server sync) MXLogDebug(@"[MXSession] Resuming the events stream from %@...", self.store.eventStreamToken); @@ -1165,11 +1155,11 @@ - (void)close [peekingRooms removeAllObjects]; // Clean summaries - for (MXRoomSummary *summary in roomsSummaries.allValues) + for (MXRoomSummary *summary in roomSummaries.allValues) { [summary destroy]; } - [roomsSummaries removeAllObjects]; + [roomSummaries removeAllObjects]; // Clean notification center [_notificationCenter removeAllListeners]; @@ -1409,7 +1399,7 @@ - (void)serverSyncWithServerTimeout:(NSUInteger)serverTimeout self->firstSyncDone = YES; // Contextualise the profiling with the amount of received information - syncTaskProfile.units = syncResponse.rooms.join.count + syncResponse.rooms.invite.count + syncResponse.rooms.leave.count; + syncTaskProfile.units = syncResponse.rooms.join.count; [MXSDKOptions.sharedInstance.profiler stopMeasuringTaskWithProfile:syncTaskProfile]; } @@ -1944,6 +1934,14 @@ - (void)handleToDeviceEvent:(MXEvent *)event onComplete:(void (^)(void))onComple - (void)handleBackgroundSyncCacheIfRequiredWithCompletion:(void (^)(void))completion { + if (_state == MXSessionStateInitialSyncFailed) + { + if (completion) + { + completion(); + } + return; + } NSParameterAssert(_state == MXSessionStateStoreDataReady || _state == MXSessionStatePaused); // keep the old state to revert later @@ -2574,11 +2572,12 @@ - (MXRoom *)roomWithAlias:(NSString *)alias if (alias) { - for (MXRoomSummary *summary in roomsSummaries.allValues) + for (MXRoom *room in self.rooms) { + MXRoomSummary *summary = room.summary; if (summary.aliases && NSNotFound != [summary.aliases indexOfObject:alias]) { - theRoom = [self roomWithRoomId:summary.roomId]; + theRoom = room; break; } } @@ -2802,9 +2801,22 @@ - (MXRoom *)getOrCreateRoom:(NSString *)roomId notify:(BOOL)notify { room = [self createRoom:roomId notify:notify]; } + // also create the room summary if needed + [self getOrCreateRoomSummary:roomId]; return room; } +- (MXRoomSummary *)getOrCreateRoomSummary:(NSString *)roomId +{ + // Create the room summary if does not exist yet + MXRoomSummary *summary = [self roomSummaryWithRoomId:roomId]; + if (!summary) + { + summary = [self createRoomSummary:roomId]; + } + return summary; +} + - (MXRoom *)createRoom:(NSString *)roomId notify:(BOOL)notify { MXRoom *room = [[MXRoom alloc] initWithRoomId:roomId andMatrixSession:self]; @@ -2813,6 +2825,22 @@ - (MXRoom *)createRoom:(NSString *)roomId notify:(BOOL)notify return room; } +- (MXRoomSummary *)createRoomSummary:(NSString *)roomId +{ + MXRoomSummary *summary = [[MXRoomSummary alloc] initWithRoomId:roomId andMatrixSession:self]; + roomSummaries[roomId] = summary; + + // Update the summary if necessary + NSString *directUserId = [self directUserIdInRoom:roomId]; + if (directUserId) + { + summary.directUserId = directUserId; + [summary save:YES]; + } + + return summary; +} + - (void)addRoom:(MXRoom*)room notify:(BOOL)notify { // Register global listeners for this room @@ -2823,22 +2851,6 @@ - (void)addRoom:(MXRoom*)room notify:(BOOL)notify [rooms setObject:room forKey:room.roomId]; - // Create the room summary if does not exist yet - MXRoomSummary *summary = roomsSummaries[room.roomId]; - if (!summary) - { - summary = [[MXRoomSummary alloc] initWithRoomId:room.roomId andMatrixSession:self]; - roomsSummaries[room.roomId] = summary; - - // Update the summary if necessary - NSString *directUserId = [self directUserIdInRoom:room.roomId]; - if (directUserId) - { - summary.directUserId = directUserId; - [summary save:YES]; - } - } - if (room.accountData.virtualRoomInfo.isVirtual) { // cache this info @@ -2870,13 +2882,9 @@ - (void)removeRoom:(NSString *)roomId [listener removeSpiedRoom:room]; } - // Clean the store - [self.store deleteRoom:roomId]; - [self.aggregations resetDataInRoom:roomId]; - // And remove the room and its summary from the list [rooms removeObjectForKey:roomId]; - [roomsSummaries removeObjectForKey:roomId]; + [roomSummaries removeObjectForKey:roomId]; // Broadcast the left room [[NSNotificationCenter defaultCenter] postNotificationName:kMXSessionDidLeaveRoomNotification @@ -2885,6 +2893,10 @@ - (void)removeRoom:(NSString *)roomId kMXSessionNotificationRoomIdKey: roomId }]; } + + // Clean the store + [self.aggregations resetDataInRoom:roomId]; + [self.store deleteRoom:roomId]; } @@ -2996,25 +3008,32 @@ - (MXRoomSummary *)roomSummaryWithRoomId:(NSString*)roomId if (roomId) { - roomSummary = roomsSummaries[roomId]; + roomSummary = roomSummaries[roomId]; + + if (roomSummary == nil) + { + // summary not in the cache, try to load it from the store + id roomSummaryProtocol = [self.store.roomSummaryStore summaryOfRoom:roomId]; + if (roomSummaryProtocol) + { + roomSummary = [[MXRoomSummary alloc] initWithSummaryModel:roomSummaryProtocol]; + [roomSummary setMatrixSession:self]; + roomSummaries[roomId] = roomSummary; + } + } } return roomSummary; } -- (NSArray*)roomsSummaries -{ - return [roomsSummaries allValues]; -} - -(void)resetRoomsSummariesLastMessage { MXLogDebug(@"[MXSession] resetRoomsSummariesLastMessage"); - for (MXRoomSummary *summary in self.roomsSummaries) + for (MXRoom *room in self.rooms) { - [summary resetLastMessage:nil failure:^(NSError *error) { - MXLogDebug(@"[MXSession] Cannot reset last message for room %@", summary.roomId); + [room.summary resetLastMessage:nil failure:^(NSError *error) { + MXLogDebug(@"[MXSession] Cannot reset last message for room %@", room.roomId); } commit:NO]; } @@ -3053,8 +3072,15 @@ - (void)fixRoomsSummariesLastMessageWithMaxServerPaginationCount:(NSUInteger)max dispatch_group_t dispatchGroup = dispatch_group_create(); - for (MXRoomSummary *summary in self.roomsSummaries) + for (MXRoom *room in self.rooms) { + MXRoomSummary *summary = room.summary; + + if (summary == nil) + { + continue; + } + // ignore this room if there is no change if (!force && summary.storedHash == summary.hash) { @@ -3621,11 +3647,12 @@ - (NSUInteger)missedNotificationsCount NSUInteger notificationCount = 0; // Sum here all the notification counts from room summaries. - for (MXRoomSummary *roomSummary in self.roomsSummaries) + for (MXRoom *room in self.rooms) { - if (roomSummary.notificationCount) + MXRoomSummary *summary = room.summary; + if (summary.notificationCount) { - notificationCount += roomSummary.notificationCount; + notificationCount += summary.notificationCount; } } @@ -3637,9 +3664,9 @@ - (NSUInteger)missedDiscussionsCount NSUInteger roomCount = 0; // Sum here all the rooms with missed notifications. - for (MXRoomSummary *roomSummary in self.roomsSummaries) + for (MXRoom *room in self.rooms) { - if (roomSummary.notificationCount) + if (room.summary.notificationCount) { roomCount ++; } @@ -3653,9 +3680,9 @@ - (NSUInteger)missedHighlightDiscussionsCount NSUInteger roomCount = 0; // Sum here all the rooms with unread highlighted messages. - for (MXRoomSummary *roomSummary in self.roomsSummaries) + for (MXRoom *room in self.rooms) { - if (roomSummary.highlightCount) + if (room.summary.highlightCount) { roomCount ++; } @@ -3667,9 +3694,9 @@ - (NSUInteger)missedHighlightDiscussionsCount - (void)markAllMessagesAsRead { // Reset the unread count in all the existing room summaries. - for (MXRoomSummary *roomSummary in self.roomsSummaries) + for (MXRoom *room in self.rooms) { - [roomSummary markAllAsRead]; + [room.summary markAllAsRead]; } } @@ -4807,7 +4834,7 @@ - (void)spaceServiceDidBuildSpaceGraph:(NSNotification *)notification [self.spaceService.ancestorsPerRoomId enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull roomId, NSSet * _Nonnull parentIds, BOOL * _Nonnull stop) { - self->roomsSummaries[roomId].parentSpaceIds = parentIds; + self->roomSummaries[roomId].parentSpaceIds = parentIds; }]; } diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index 0c86c54a0c..df2ff158ef 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -98,7 +98,7 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXEventAnnotationChunk.h" #import "MXEventAnnotation.h" #import "MXEventReferenceChunk.h" - +#import "MXEventReplace.h" #import "MXReplyEventParser.h" diff --git a/MatrixSDK/MatrixSDKVersion.m b/MatrixSDK/MatrixSDKVersion.m index 9807995797..08140f6822 100644 --- a/MatrixSDK/MatrixSDKVersion.m +++ b/MatrixSDK/MatrixSDKVersion.m @@ -16,4 +16,4 @@ #import -NSString *const MatrixSDKVersion = @"0.20.16"; +NSString *const MatrixSDKVersion = @"0.21.0"; diff --git a/MatrixSDK/Room/Polls/PollAggregator.swift b/MatrixSDK/Room/Polls/PollAggregator.swift index 10880a80c5..9e154a7966 100644 --- a/MatrixSDK/Room/Polls/PollAggregator.swift +++ b/MatrixSDK/Room/Polls/PollAggregator.swift @@ -36,14 +36,22 @@ public protocol PollAggregatorDelegate: AnyObject { public class PollAggregator { + private struct Constants { + static let minAnswerOptionCount = 2 + } + private let session: MXSession private let room: MXRoom - private let pollStartEvent: MXEvent - private let pollStartEventContent: MXEventContentPollStart + private let pollStartEventId: String private let pollBuilder: PollBuilder - private var eventListener: Any! + private var pollStartEventContent: MXEventContentPollStart! + + private var referenceEventsListener: Any? + private var editEventsListener: Any? + private var events: [MXEvent] = [] + private var hasBeenEdited = false public private(set) var poll: PollProtocol! { didSet { @@ -54,27 +62,58 @@ public class PollAggregator { public var delegate: PollAggregatorDelegate? deinit { - room.removeListener(eventListener) + if let referenceEventsListener = referenceEventsListener { + room.removeListener(referenceEventsListener) + } + + if let editEventsListener = editEventsListener { + session.aggregations.removeListener(editEventsListener) + } } - public init(session: MXSession, room: MXRoom, pollStartEvent: MXEvent) throws { + public init(session: MXSession, room: MXRoom, pollStartEventId: String) throws { self.session = session self.room = room - self.pollStartEvent = pollStartEvent + self.pollStartEventId = pollStartEventId + self.pollBuilder = PollBuilder() - guard let pollStartEventContent = MXEventContentPollStart(fromJSON: pollStartEvent.content) else { - throw PollAggregatorError.invalidPollStartEvent + NotificationCenter.default.addObserver(self, selector: #selector(handleRoomDataFlush), name: NSNotification.Name.mxRoomDidFlushData, object: self.room) + + editEventsListener = session.aggregations.listenToEditsUpdate(inRoom: self.room.roomId) { [weak self] event in + guard let self = self, + self.pollStartEventId == event.relatesTo.eventId + else { + return + } + + do { + try self.buildPollStartContent() + } catch { + self.delegate?.pollAggregator(self, didFailWithError: PollAggregatorError.invalidPollStartEvent) + } } - self.pollStartEventContent = pollStartEventContent + try buildPollStartContent() + } + + private func buildPollStartContent() throws { + guard let event = session.store.event(withEventId: pollStartEventId, inRoom: room.roomId), + let eventContent = MXEventContentPollStart(fromJSON: event.content), + eventContent.answerOptions.count >= Constants.minAnswerOptionCount + else { + throw PollAggregatorError.invalidPollStartEvent + } - pollBuilder = PollBuilder() + pollStartEventContent = eventContent - poll = pollBuilder.build(pollStartEventContent: self.pollStartEventContent, events: self.events, currentUserIdentifier: session.myUserId) + hasBeenEdited = (event.unsignedData.relations?.replace != nil) - reloadData() + poll = pollBuilder.build(pollStartEventContent: eventContent, + events: events, + currentUserIdentifier: session.myUserId, + hasBeenEdited: hasBeenEdited) - NotificationCenter.default.addObserver(self, selector: #selector(handleRoomDataFlush), name: NSNotification.Name.mxRoomDidFlushData, object: self.room) + reloadPollData() } @objc private func handleRoomDataFlush(sender: Notification) { @@ -82,12 +121,13 @@ public class PollAggregator { return } - reloadData() + reloadPollData() } - private func reloadData() { + private func reloadPollData() { delegate?.pollAggregatorDidStartLoading(self) - session.aggregations.referenceEvents(forEvent: pollStartEvent.eventId, inRoom: room.roomId, from: nil, limit: -1) { [weak self] response in + + session.aggregations.referenceEvents(forEvent: pollStartEventId, inRoom: room.roomId, from: nil, limit: -1) { [weak self] response in guard let self = self else { return } @@ -97,20 +137,26 @@ public class PollAggregator { self.events.append(contentsOf: response.chunk) let eventTypes = [kMXEventTypeStringPollResponse, kMXEventTypeStringPollResponseMSC3381, kMXEventTypeStringPollEnd, kMXEventTypeStringPollEndMSC3381] - self.eventListener = self.room.listen(toEventsOfTypes: eventTypes) { [weak self] event, direction, state in + self.referenceEventsListener = self.room.listen(toEventsOfTypes: eventTypes) { [weak self] event, direction, state in guard let self = self, let event = event, let relatedEventId = event.relatesTo?.eventId, - relatedEventId == self.pollStartEvent.eventId else { + relatedEventId == self.pollStartEventId else { return } self.events.append(event) - self.poll = self.pollBuilder.build(pollStartEventContent: self.pollStartEventContent, events: self.events, currentUserIdentifier: self.session.myUserId) + self.poll = self.pollBuilder.build(pollStartEventContent: self.pollStartEventContent, + events: self.events, + currentUserIdentifier: self.session.myUserId, + hasBeenEdited: self.hasBeenEdited) } as Any - self.poll = self.pollBuilder.build(pollStartEventContent: self.pollStartEventContent, events: self.events, currentUserIdentifier: self.session.myUserId) + self.poll = self.pollBuilder.build(pollStartEventContent: self.pollStartEventContent, + events: self.events, + currentUserIdentifier: self.session.myUserId, + hasBeenEdited: self.hasBeenEdited) self.delegate?.pollAggregatorDidEndLoading(self) diff --git a/MatrixSDK/Room/Polls/PollBuilder.swift b/MatrixSDK/Room/Polls/PollBuilder.swift index 5d5f521cde..401ac6aeb2 100644 --- a/MatrixSDK/Room/Polls/PollBuilder.swift +++ b/MatrixSDK/Room/Polls/PollBuilder.swift @@ -17,16 +17,22 @@ import Foundation struct PollBuilder { - func build(pollStartEventContent: MXEventContentPollStart, events: [MXEvent], currentUserIdentifier: String) -> PollProtocol { + + private struct Constants { + static let maxAnswerOptionCount = 20 + } + + func build(pollStartEventContent: MXEventContentPollStart, events: [MXEvent], currentUserIdentifier: String, hasBeenEdited: Bool = false) -> PollProtocol { let poll = Poll() + poll.hasBeenEdited = hasBeenEdited poll.text = pollStartEventContent.question poll.maxAllowedSelections = max(1, pollStartEventContent.maxSelections.uintValue) poll.kind = (pollStartEventContent.kind == kMXMessageContentKeyExtensiblePollKindUndisclosed ? .undisclosed : .disclosed) var answerOptionIdentifiers = [String]() - poll.answerOptions = pollStartEventContent.answerOptions.map { answerOption in + poll.answerOptions = pollStartEventContent.answerOptions.prefix(Constants.maxAnswerOptionCount).map { answerOption in answerOptionIdentifiers.append(answerOption.uuid) let option = PollAnswerOption() @@ -41,8 +47,8 @@ struct PollBuilder { var filteredEvents = events.filter { event in guard let eventContent = event.content, event.eventType == __MXEventType.pollResponse, - let answer = eventContent[kMXMessageContentKeyExtensiblePollResponse] as? [String: [String]], - let _ = answer[kMXMessageContentKeyExtensiblePollAnswers] else { + let response = pollResponseFromEventContent(eventContent), + let _ = response[kMXMessageContentKeyExtensiblePollAnswers] else { return false } @@ -62,8 +68,8 @@ struct PollBuilder { let answersGroupedByUser = filteredEvents.reduce([String: [String]]()) { result, event in guard let userIdentifier = event.sender, let eventContent = event.content, - let answer = eventContent[kMXMessageContentKeyExtensiblePollResponse] as? [String: [String]], - let answerIdentifiers = answer[kMXMessageContentKeyExtensiblePollAnswers], + let response = pollResponseFromEventContent(eventContent), + let answerIdentifiers = response[kMXMessageContentKeyExtensiblePollAnswers], !result.keys.contains(userIdentifier) else { return result } @@ -111,4 +117,13 @@ struct PollBuilder { return poll } + private func pollResponseFromEventContent(_ eventContent: [String: Any]) -> [String: [String]]? { + if let response = eventContent[kMXMessageContentKeyExtensiblePollResponse] { + return response as? [String: [String]] + } else if let response = eventContent[kMXMessageContentKeyExtensiblePollResponseMSC3381] { + return response as? [String: [String]] + } + + return nil; + } } diff --git a/MatrixSDK/Room/Polls/PollModels.swift b/MatrixSDK/Room/Polls/PollModels.swift index 6fee43b397..4d426b45cb 100644 --- a/MatrixSDK/Room/Polls/PollModels.swift +++ b/MatrixSDK/Room/Polls/PollModels.swift @@ -36,6 +36,7 @@ public protocol PollProtocol { var maxAllowedSelections: UInt { get } var isClosed: Bool { get } var totalAnswerCount: UInt { get } + var hasBeenEdited: Bool { get } } class PollAnswerOption: PollAnswerOptionProtocol { @@ -54,6 +55,7 @@ class Poll: PollProtocol { var kind: PollKind = .disclosed var maxAllowedSelections: UInt = 1 var isClosed: Bool = false + var hasBeenEdited: Bool = false var totalAnswerCount: UInt { return self.answerOptions.reduce(0) { $0 + $1.count} diff --git a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h index bec6393501..12fe87af35 100644 --- a/MatrixSDK/VoIP/CallStack/MXCallStackCall.h +++ b/MatrixSDK/VoIP/CallStack/MXCallStackCall.h @@ -151,19 +151,9 @@ NS_ASSUME_NONNULL_BEGIN /** Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. - @param duration Duration for each character of tones (in milliseconds). - Allowed interval is from 70 ms to 6000 ms inclusively. - If given value is outside of these limits, it'll be limited to them. - Pass 0 to use default value or last used value. - @param interToneGap Duration for gap between each character of tones (in milliseconds). - Must be at least 50 ms. - If given value is lower than 50 ms, it'll be limited to that value. - Pass 0 to use default value or last used value. @returns Whether the operation succeeded or not. */ -- (BOOL)sendDTMF:(NSString * _Nonnull)tones - duration:(NSUInteger)duration - interToneGap:(NSUInteger)interToneGap; +- (BOOL)sendDTMF:(NSString * _Nonnull)tones; #pragma mark - Properties /** diff --git a/MatrixSDK/VoIP/MXCall.h b/MatrixSDK/VoIP/MXCall.h index 03f6e6fe92..3304210661 100644 --- a/MatrixSDK/VoIP/MXCall.h +++ b/MatrixSDK/VoIP/MXCall.h @@ -217,19 +217,9 @@ extern NSString *const kMXCallSupportsTransferringStatusDidChange; /** Creates a task to send given DTMF tones in the call. If there is a task already running, it'll be canceled. @param tones DTMF tones to be sent. Allowed characters: [0-9], [A-D], '#', `*`. Case insensitive. Comma (',') will cause a 2 seconds delay before sending next character. - @param duration Duration for each character of tones (in milliseconds). - Allowed interval is from 70 ms to 6000 ms inclusively. - If given value is outside of these limits, it'll be limited to them. - Pass 0 to use default value or last used value. - @param interToneGap Duration for gap between each character of tones (in milliseconds). - Must be at least 50 ms. - If given value is lower than 50 ms, it'll be limited to that value. - Pass 0 to use default value or last used value. @returns Whether the operation succeeded or not. */ -- (BOOL)sendDTMF:(NSString * _Nonnull)tones - duration:(NSUInteger)duration - interToneGap:(NSUInteger)interToneGap; +- (BOOL)sendDTMF:(NSString * _Nonnull)tones; #pragma mark - Properties /** diff --git a/MatrixSDK/VoIP/MXCall.m b/MatrixSDK/VoIP/MXCall.m index 9b610aa9c3..d4a269a991 100644 --- a/MatrixSDK/VoIP/MXCall.m +++ b/MatrixSDK/VoIP/MXCall.m @@ -746,10 +746,8 @@ - (BOOL)supportsDTMF } - (BOOL)sendDTMF:(NSString * _Nonnull)tones - duration:(NSUInteger)duration - interToneGap:(NSUInteger)interToneGap { - return [callStackCall sendDTMF:tones duration:duration interToneGap:interToneGap]; + return [callStackCall sendDTMF:tones]; } #pragma mark - Properties diff --git a/MatrixSDK/VoIP/MXCallManager.m b/MatrixSDK/VoIP/MXCallManager.m index 5023859d40..9afe553984 100644 --- a/MatrixSDK/VoIP/MXCallManager.m +++ b/MatrixSDK/VoIP/MXCallManager.m @@ -1381,12 +1381,11 @@ - (void)conferenceUserRoomForRoom:(NSString*)roomId __block MXRoom *conferenceUserRoom; dispatch_group_t group = dispatch_group_create(); - for (MXRoomSummary *roomSummary in _mxSession.roomsSummaries) + for (MXRoom *room in _mxSession.rooms) { - if (roomSummary.isConferenceUserRoom) + if (room.summary.isConferenceUserRoom) { dispatch_group_enter(group); - MXRoom *room = [_mxSession roomWithRoomId:roomSummary.roomId]; [room state:^(MXRoomState *roomState) { if ([roomState.members memberWithUserId:conferenceUserId]) diff --git a/MatrixSDK/VoIP/MXiOSAudioOutputRouter.swift b/MatrixSDK/VoIP/MXiOSAudioOutputRouter.swift index 63db3d1bb8..da98c1ed02 100644 --- a/MatrixSDK/VoIP/MXiOSAudioOutputRouter.swift +++ b/MatrixSDK/VoIP/MXiOSAudioOutputRouter.swift @@ -93,7 +93,7 @@ public class MXiOSAudioOutputRouter: NSObject { } /// Attempt to override route type to given type. - /// - Parameter routeType: Desired route type. `external` is useless if no external device connected, then it would fallback to the default route type. + /// - Parameter route: Desired route. If `nil` passed, then it would fallback to the default route. public func changeCurrentRoute(to route: MXiOSAudioOutputRoute?) { if let route = route { updateRoute(to: route) diff --git a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m index 31a41d5b14..b7097800ee 100644 --- a/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m +++ b/MatrixSDKExtensions/VoIP/Jingle/MXJingleCallStackCall.m @@ -447,19 +447,21 @@ - (void)handleAnswer:(NSString *)sdp success:(void (^)(void))success failure:(vo - (BOOL)canSendDTMF { - id dtmfSender = peerConnection.senders.firstObject.dtmfSender; - - if (dtmfSender == nil) + return [self dtmfSender]; +} + +- (id)dtmfSender +{ + for(RTCRtpSender *sender in peerConnection.senders) { - return NO; + if ([sender.track.kind isEqualToString: kRTCMediaStreamTrackKindAudio] && sender.dtmfSender.canInsertDtmf) { + return sender.dtmfSender; + } } - - return dtmfSender.canInsertDtmf; + return nil; } - (BOOL)sendDTMF:(NSString *)tones - duration:(NSUInteger)duration - interToneGap:(NSUInteger)interToneGap; { if (!self.canSendDTMF) { @@ -467,34 +469,7 @@ - (BOOL)sendDTMF:(NSString *)tones return NO; } - id dtmfSender = peerConnection.senders.firstObject.dtmfSender; - - if (duration == 0) - { - // use default or last used value - duration = dtmfSender.duration; - } - else - { - // limit lower bound - duration = MAX(duration, 70); - - // limit upper bound - duration = MIN(duration, 6000); - } - - if (interToneGap == 0) - { - // use default or last used value - interToneGap = dtmfSender.interToneGap; - } - else - { - // limit lower bound - interToneGap = MAX(interToneGap, 50); - } - - return [dtmfSender insertDtmf:tones duration:duration interToneGap:interToneGap]; + return [[self dtmfSender] insertDtmf:tones duration:.1 interToneGap:0.07]; } #pragma mark - RTCPeerConnectionDelegate diff --git a/MatrixSDKTests/DirectRoomTests.m b/MatrixSDKTests/DirectRoomTests.m index 7bd2ca250d..7be0c733fe 100644 --- a/MatrixSDKTests/DirectRoomTests.m +++ b/MatrixSDKTests/DirectRoomTests.m @@ -416,7 +416,7 @@ - (void)testSummaryStorage [bobSession close]; // Check content from the store - [store asyncRoomsSummaries:^(NSArray * _Nonnull roomsSummaries) { + [store.roomSummaryStore fetchAllSummaries:^(NSArray * _Nonnull roomsSummaries) { // Test for checking the test XCTAssertEqual(roomsSummaries.count, 1); @@ -428,9 +428,6 @@ - (void)testSummaryStorage [expectation fulfill]; - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; }]; } failure:^(NSError *error) { @@ -479,7 +476,7 @@ - (void)testSummaryAfterInitialSyncAndStorage [bobSession2 close]; // Check content from the store - [store2 asyncRoomsSummaries:^(NSArray * _Nonnull roomsSummaries) { + [store2.roomSummaryStore fetchAllSummaries:^(NSArray * _Nonnull roomsSummaries) { // Test for checking the test XCTAssertEqual(roomsSummaries.count, 1); @@ -491,9 +488,6 @@ - (void)testSummaryAfterInitialSyncAndStorage [expectation fulfill]; - } failure:^(NSError *error) { - XCTFail(@"The operation should not fail - NSError: %@", error); - [expectation fulfill]; }]; } failure:^(NSError *error) { diff --git a/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift b/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift new file mode 100644 index 0000000000..79fe5262f7 --- /dev/null +++ b/MatrixSDKTests/MXCoreDataRoomListDataManagerUnitTests.swift @@ -0,0 +1,257 @@ +// +// Copyright 2021 The Matrix.org Foundation C.I.C +// +// 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 MatrixSDK + +class MXCoreDataRoomListDataManagerUnitTests: XCTestCase { + + private static var fetcher: MXRoomListDataFetcher? + private static var delegate: MXRoomListDataFetcherDelegate? + + private enum Constants { + static var credentials: MXCredentials { + let result = MXCredentials(homeServer: "localhost", + userId: "@some_user_id:some_domain.com", + accessToken: "some_access_token") + result.deviceId = "some_device_id" + return result + } + } + + override class func setUp() { + MXSDKOptions.sharedInstance().roomListDataManagerClass = MXCoreDataRoomListDataManager.self + MXRealmCryptoStore.deleteAllStores() + } + + override class func tearDown() { + fetcher?.stop() + fetcher = nil + delegate = nil + } + + private var basicFetchOptions: MXRoomListDataFetchOptions { + let filterOptions = MXRoomListDataFilterOptions(showAllRoomsInHomeSpace: true) + let sortOptions = MXRoomListDataSortOptions(missedNotificationsFirst: false, unreadMessagesFirst: false) + return MXRoomListDataFetchOptions(filterOptions: filterOptions, + sortOptions: sortOptions) + } + + // MARK - Tests + + func testPaginationOptionsInit() { + let options1 = MXRoomListDataPaginationOptions.none + let options2 = MXRoomListDataPaginationOptions(rawValue: MXRoomListDataPaginationOptions.NoneValue) + XCTAssertEqual(options1, options2, "Pagination options should be equal") + + let options3 = MXRoomListDataPaginationOptions.default + let options4 = MXRoomListDataPaginationOptions(rawValue: MXRoomListDataPaginationOptions.DefaultValue) + XCTAssertEqual(options3, options4, "Pagination options should be equal") + + let options5 = MXRoomListDataPaginationOptions(rawValue: 5) + let options6 = MXRoomListDataPaginationOptions.custom(5) + XCTAssertEqual(options5, options6, "Pagination options should be equal") + } + + func testFilterOptionsInit() { + let filterOptions = MXRoomListDataFilterOptions(showAllRoomsInHomeSpace: false) + XCTAssertTrue(filterOptions.dataTypes.isEmpty, "Default data types should be empty") + XCTAssertEqual(filterOptions.notDataTypes, [.hidden, .conferenceUser, .space], "Default not data types should be provided") + XCTAssertFalse(filterOptions.onlySuggested, "Default filter options should not include onlySuggested") + XCTAssertNil(filterOptions.query, "Default filter options should not include query") + XCTAssertNil(filterOptions.space, "Default filter options should not include space") + } + + func testSortOptionsInit() { + let missedNotificationsFirst = true + let unreadMessagesFirst = true + let sortOptions = MXRoomListDataSortOptions(missedNotificationsFirst: missedNotificationsFirst, + unreadMessagesFirst: unreadMessagesFirst) + XCTAssertEqual(missedNotificationsFirst, sortOptions.missedNotificationsFirst, "Sort options should persist missedNotificationsFirst") + XCTAssertEqual(unreadMessagesFirst, sortOptions.unreadMessagesFirst, "Sort options should persist unreadMessagesFirst") + XCTAssertTrue(sortOptions.invitesFirst, "Default sort options should include invitesFirst") + XCTAssertTrue(sortOptions.sentStatus, "Default sort options should include sentStatus") + XCTAssertTrue(sortOptions.lastEventDate, "Default sort options should include lastEventDate") + XCTAssertFalse(sortOptions.favoriteTag, "Default sort options should not include favoriteTag") + XCTAssertTrue(sortOptions.suggested, "Default sort options should include suggested") + } + + func testFetchOptionsInit() { + let fetchOptions = self.basicFetchOptions + XCTAssertEqual(fetchOptions.async, true, "Fetch options should include async") + XCTAssertEqual(fetchOptions.paginationOptions, .default, "Default fetch options should enable pagination") + XCTAssertNil(fetchOptions.fetcher, "Fetch options should not include fetcher without initializing a fetcher") + } + + func testDataManagerInitStandalone() { + let manager = MXStoreRoomListDataManager() + let restClient = MXRestClient(credentials: Constants.credentials, unrecognizedCertificateHandler: nil) + guard let session = MXSession(matrixRestClient: restClient) else { + XCTFail("Failed to setup test conditions") + return + } + defer { + session.close() + } + wait { expectation in + session.setStore(MXFileStore(), completion: { response in + switch response { + case .success: + manager.configure(withSession: session) + XCTAssertEqual(manager.session, session, "Manager should persist session") + let fetchOptions = self.basicFetchOptions + let fetcher = manager.fetcher(withOptions: fetchOptions) + XCTAssertEqual(fetcher.fetchOptions, fetchOptions, "Fetch options should be persisted in fetcher") + + session.close() + expectation.fulfill() + case .failure(let error): + XCTFail("Failed to setup test conditions: \(error)") + return + } + }) + } + } + + func testDataManagerInitFromSession() { + let restClient = MXRestClient(credentials: Constants.credentials, unrecognizedCertificateHandler: nil) + guard let session = MXSession(matrixRestClient: restClient) else { + XCTFail("Failed to setup test conditions") + return + } + XCTAssertNil(session.roomListDataManager, "Room list data manager should be created after setting the store") + defer { + session.close() + } + + wait { expectation in + session.setStore(MXFileStore(), completion: { response in + switch response { + case .success: + guard let manager = session.roomListDataManager else { + XCTFail("Failed to setup test conditions") + return + } + XCTAssertEqual(manager.session, session, "Manager should persist session") + let fetchOptions = self.basicFetchOptions + let fetcher = manager.fetcher(withOptions: fetchOptions) + XCTAssertEqual(fetcher.fetchOptions, fetchOptions, "Fetch options should be persisted in fetcher") + + session.close() + expectation.fulfill() + case .failure(let error): + XCTFail("Failed to setup test conditions: \(error)") + return + } + }) + } + } + + func testFilterDataTypes() { + let fetcher = generateDefaultFetcher() + Self.fetcher = fetcher + self.wait { expectation in + let delegate = MockRoomListDataFetcherDelegate(withBlock: { + XCTAssertEqual(fetcher.data?.counts.numberOfRooms, 10, "Fetcher should fetch all rooms except types: [.hidden, .conferenceUser, .space]") + XCTAssertEqual(fetcher.data?.counts.total?.numberOfRooms, 60, "Fetcher should be aware of all rooms except types: [.hidden, .conferenceUser, .space]") + + expectation.fulfill() + }) + Self.delegate = delegate + fetcher.addDelegate(delegate) + } + + fetcher.paginate() + } + + private func generateDefaultFetcher() -> MXRoomListDataFetcher { + let store = MXCoreDataRoomSummaryStore(withCredentials: Constants.credentials) + let roomSummaries = generateMockRoomSummaries() + XCTAssertEqual(roomSummaries.count, 90, "Generator must generate 90 rooms in total") + // insert all rooms into the store + for summary in roomSummaries { + store.storeSummary(summary) + } + + let fetcher = MXCoreDataRoomListDataFetcher(session: nil, + fetchOptions: basicFetchOptions, + store: store) + return fetcher + } + + /// Generates 10 rooms per each type. Generates 90 rooms by default. Sorted by data types and lastEventDate ascending. + private func generateMockRoomSummaries(numberOfRoomsPerType: [Int] = Array(repeating: 10, count: MXRoomSummaryDataTypes.all.count), + numberOfUntyped: Int = 10) -> [MXRoomSummaryProtocol] { + var result: [MockRoomSummary] = [] + + for (index, numberOfRooms) in numberOfRoomsPerType.enumerated() { + let safeIndex = index % MXRoomSummaryDataTypes.all.count + let typed = (0.. Void) { + let waiter = XCTWaiter() + let expectation = XCTestExpectation(description: "Async operation expectation") + block(expectation) + waiter.wait(for: [expectation], timeout: timeout) + } + +} + +fileprivate extension MXRoomSummaryDataTypes { + + static let all: [MXRoomSummaryDataTypes] = [.invited, .favorited, .direct, .lowPriority, .serverNotice, .hidden, .space, .conferenceUser] + +} + +fileprivate class MockRoomListDataFetcherDelegate: MXRoomListDataFetcherDelegate { + + private var block: () -> Void + + init(withBlock block: @escaping () -> Void) { + self.block = block + } + + func fetcherDidChangeData(_ fetcher: MXRoomListDataFetcher) { + self.block() + } + +} diff --git a/MatrixSDKTests/MXPollAggregatorTests.swift b/MatrixSDKTests/MXPollAggregatorTests.swift index eafc859c00..1ed1f36e30 100644 --- a/MatrixSDKTests/MXPollAggregatorTests.swift +++ b/MatrixSDKTests/MXPollAggregatorTests.swift @@ -35,7 +35,7 @@ class MXPollAggregatorTest: XCTestCase { func testAggregations() { self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in - self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEvent: pollStartEvent) + self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId) let dispatchGroup = DispatchGroup() @@ -68,7 +68,7 @@ class MXPollAggregatorTest: XCTestCase { func testSessionPausing() { self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in - self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEvent: pollStartEvent) + self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId) XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 1) // One from Alice XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 0) @@ -92,7 +92,7 @@ class MXPollAggregatorTest: XCTestCase { func testGappySync() { self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in - self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEvent: pollStartEvent) + self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId) XCTAssertEqual(self.pollAggregator.poll.answerOptions.first!.count, 1) // One from Alice XCTAssertEqual(self.pollAggregator.poll.answerOptions.last!.count, 0) @@ -125,6 +125,34 @@ class MXPollAggregatorTest: XCTestCase { } } + func testEditing() { + self.createScenarioForBobAndAlice { bobSession, aliceSession, bobRoom, aliceRoom, pollStartEvent, expectation in + self.pollAggregator = try! PollAggregator(session: bobSession, room: bobRoom, pollStartEventId: pollStartEvent.eventId) + + let oldContent = MXEventContentPollStart(fromJSON: pollStartEvent.content)! + let newContent = MXEventContentPollStart(question: "Some other question", + kind: oldContent.kind, + maxSelections: oldContent.maxSelections, + answerOptions: []) + + self.pollAggregator.delegate = PollAggregatorBlockWrapper(dataUpdateCallback: { + + XCTAssertEqual(self.pollAggregator.poll.text, "Some other question") + XCTAssertEqual(self.pollAggregator.poll.answerOptions.count, 0) + XCTAssertTrue(self.pollAggregator.poll.hasBeenEdited) + + expectation.fulfill() + self.pollAggregator.delegate = nil + }) + + bobRoom.sendPollUpdate(for: pollStartEvent, oldContent: oldContent, newContent: newContent, localEcho: nil) { result in + + } failure: { error in + XCTFail("The operation should not fail - NSError: \(String(describing: error))") + } + } + } + // MARK: - Private func createScenarioForBobAndAlice(_ readyToTest: @escaping (MXSession, MXSession, MXRoom, MXRoom, MXEvent, XCTestExpectation) -> Void) { diff --git a/MatrixSDKTests/MXRoomListDataManagerTests.swift b/MatrixSDKTests/MXRoomListDataManagerTests.swift index 26c5613a75..eb847f4cb5 100644 --- a/MatrixSDKTests/MXRoomListDataManagerTests.swift +++ b/MatrixSDKTests/MXRoomListDataManagerTests.swift @@ -27,6 +27,7 @@ class MXRoomListDataManagerTests: XCTestCase { } override func setUp() { + MXSDKOptions.sharedInstance().roomListDataManagerClass = MXStoreRoomListDataManager.self testData = MatrixSDKTestsData() } @@ -251,7 +252,7 @@ class MXRoomListDataManagerTests: XCTestCase { // MARK: - Private private func createBasicFetcherWithBob(_ completion: @escaping (MXSession, MXRoom, MXRoomListDataFetcher, XCTestExpectation) -> Void) { - let store = MXFileStore() + let store = MXMemoryStore() testData.doMXSessionTest(withBobAndARoom: self, andStore: store) { bobSession, initialRoom, expectation in guard let bobSession = bobSession, let initialRoom = initialRoom, @@ -271,7 +272,7 @@ class MXRoomListDataManagerTests: XCTestCase { } private func createBasicFetcherWithBobAndAlice(_ completion: @escaping (MXSession, MXRestClient, MXRoomListDataFetcher, XCTestExpectation) -> Void) { - testData.doMXSessionTestWithBobAndAlice(inARoom: self, andStore: MXFileStore()) { bobSession, aliceRestClient, roomId, expectation in + testData.doMXSessionTestWithBobAndAlice(inARoom: self, andStore: MXMemoryStore()) { bobSession, aliceRestClient, roomId, expectation in guard let bobSession = bobSession, let aliceRestClient = aliceRestClient, let expectation = expectation else { diff --git a/MatrixSDKTests/MXRoomSummaryTests.m b/MatrixSDKTests/MXRoomSummaryTests.m index b54cd5f3c7..ac64781617 100644 --- a/MatrixSDKTests/MXRoomSummaryTests.m +++ b/MatrixSDKTests/MXRoomSummaryTests.m @@ -1197,7 +1197,7 @@ - (void)testDoNotStoreDecryptedData [aliceSession2 start:^{ - MXRoomSummary *summary2 = [aliceSession2.store summaryOfRoom:roomId]; + MXRoomSummary *summary2 = [aliceSession2.store.roomSummaryStore summaryOfRoom:roomId]; XCTAssert(summary2.isEncrypted); XCTAssertEqualObjects(summary2.lastMessage.eventId, lastMessageEventId); diff --git a/MatrixSDKTests/MXSessionTests.m b/MatrixSDKTests/MXSessionTests.m index ddd4af7711..a3ed722142 100644 --- a/MatrixSDKTests/MXSessionTests.m +++ b/MatrixSDKTests/MXSessionTests.m @@ -465,7 +465,7 @@ - (void)testCloseWithMXMemoryStore [mxSession start:^{ - NSUInteger storeRoomsCount = store.rooms.count; + NSUInteger storeRoomsCount = store.roomSummaryStore.rooms.count; XCTAssertGreaterThan(storeRoomsCount, 0); @@ -477,7 +477,7 @@ - (void)testCloseWithMXMemoryStore // Check the stream has been correctly shutdowned. Checking that the store has not changed is one way to verify it dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - XCTAssertEqual(store.rooms.count, storeRoomsCount, @"There must still the same number of stored rooms"); + XCTAssertEqual(store.roomSummaryStore.rooms.count, storeRoomsCount, @"There must still the same number of stored rooms"); [expectation fulfill]; }); diff --git a/MatrixSDKTests/MXRoomListDataManagerUnitTests.swift b/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift similarity index 98% rename from MatrixSDKTests/MXRoomListDataManagerUnitTests.swift rename to MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift index 364d478c57..45606f083e 100644 --- a/MatrixSDKTests/MXRoomListDataManagerUnitTests.swift +++ b/MatrixSDKTests/MXStoreRoomListDataManagerUnitTests.swift @@ -18,7 +18,7 @@ import XCTest @testable import MatrixSDK -class MXRoomListDataManagerUnitTests: XCTestCase { +class MXStoreRoomListDataManagerUnitTests: XCTestCase { private enum Constants { static var credentials: MXCredentials { @@ -32,6 +32,7 @@ class MXRoomListDataManagerUnitTests: XCTestCase { } override class func setUp() { + MXSDKOptions.sharedInstance().roomListDataManagerClass = MXStoreRoomListDataManager.self MXRealmCryptoStore.deleteAllStores() } @@ -40,6 +41,7 @@ class MXRoomListDataManagerUnitTests: XCTestCase { let sortOptions = MXRoomListDataSortOptions(missedNotificationsFirst: false, unreadMessagesFirst: false) return MXRoomListDataFetchOptions(filterOptions: filterOptions, sortOptions: sortOptions, + paginationOptions: .none, async: false) } @@ -253,7 +255,7 @@ class MXRoomListDataManagerUnitTests: XCTestCase { XCTAssertEqual(roomSummaries.count, 90, "Generator must generate 90 rooms in total") // insert all rooms into the store for summary in roomSummaries { - store.storeSummary(forRoom: summary.roomId, summary: summary) + store.roomSummaryStore.storeSummary(summary) } self.wait { expectation in diff --git a/MatrixSDKTests/MXStoreTests.m b/MatrixSDKTests/MXStoreTests.m index d445918616..d31d841ea0 100644 --- a/MatrixSDKTests/MXStoreTests.m +++ b/MatrixSDKTests/MXStoreTests.m @@ -1178,7 +1178,7 @@ - (void)checkMXSessionOnStoreDataReady:(Class)mxStoreClass MXUser *myUser = [store userWithUserId:matrixSDKTestsData.bobCredentials.userId]; XCTAssertNil(myUser); - XCTAssertEqual(store.rooms.count, 0); + XCTAssertEqual(store.roomSummaryStore.rooms.count, 0); if ([store respondsToSelector:@selector(close)]) { @@ -1194,7 +1194,7 @@ - (void)checkMXSessionOnStoreDataReady:(Class)mxStoreClass [mxSession start:^{ NSString *eventStreamToken = [store.eventStreamToken copy]; - NSUInteger storeRoomsCount = store.rooms.count; + NSUInteger storeRoomsCount = store.roomSummaryStore.rooms.count; [mxSession close]; mxSession = nil; @@ -1216,7 +1216,7 @@ - (void)checkMXSessionOnStoreDataReady:(Class)mxStoreClass onStoreDataReadyCalled = YES; XCTAssertEqual(mxSession2.rooms.count, storeRoomsCount, @"MXSessionOnStoreDataReady must have loaded as many MXRooms as room stored"); - XCTAssertEqual(store2.rooms.count, storeRoomsCount, @"There must still the same number of stored rooms"); + XCTAssertEqual(store2.roomSummaryStore.rooms.count, storeRoomsCount, @"There must still the same number of stored rooms"); XCTAssertEqualObjects(eventStreamToken, store2.eventStreamToken, @"The event stream token must not have changed yet"); [mxSession2 start:^{ @@ -1224,7 +1224,7 @@ - (void)checkMXSessionOnStoreDataReady:(Class)mxStoreClass XCTAssert(onStoreDataReadyCalled, @"onStoreDataReady must alway be called before onServerSyncDone"); XCTAssertEqual(mxSession2.rooms.count, storeRoomsCount + 1, @"MXSessionOnStoreDataReady must have loaded as many MXRooms as room stored"); - XCTAssertEqual(store2.rooms.count, storeRoomsCount + 1, @"There must still the same number of stored rooms"); + XCTAssertEqual(store2.roomSummaryStore.rooms.count, storeRoomsCount + 1, @"There must still the same number of stored rooms"); XCTAssertNotEqualObjects(eventStreamToken, store2.eventStreamToken, @"The event stream token must not have changed yet"); [mxSession2 close]; @@ -1287,7 +1287,7 @@ - (void)checkRoomDeletion:(Class)mxStoreClass MXRoom *room = [mxSession roomWithRoomId:roomId]; [room leave:^{ - XCTAssertEqual(NSNotFound, [store.rooms indexOfObject:roomId], @"The room %@ must be no more in the store", roomId); + XCTAssertEqual(NSNotFound, [store.roomSummaryStore.rooms indexOfObject:roomId], @"The room %@ must be no more in the store", roomId); [mxSession close]; mxSession = nil; @@ -1296,7 +1296,7 @@ - (void)checkRoomDeletion:(Class)mxStoreClass id store2 = [[mxStoreClass alloc] init]; [store2 openWithCredentials:matrixSDKTestsData.bobCredentials onComplete:^{ - XCTAssertEqual(NSNotFound, [store2.rooms indexOfObject:roomId], @"The room %@ must be no more in the store", roomId); + XCTAssertEqual(NSNotFound, [store2.roomSummaryStore.rooms indexOfObject:roomId], @"The room %@ must be no more in the store", roomId); if ([store2 respondsToSelector:@selector(close)]) { @@ -1489,7 +1489,7 @@ - (void)checkMultiAccount:(Class)mxStoreClass id bobStore3 = [[mxStoreClass alloc] init]; [bobStore3 openWithCredentials:matrixSDKTestsData.bobCredentials onComplete:^{ - XCTAssertEqual(bobStore2.rooms.count, bobStore3.rooms.count); + XCTAssertEqual(bobStore2.roomSummaryStore.rooms.count, bobStore3.roomSummaryStore.rooms.count); if ([bobStore2 isKindOfClass:[MXFileStore class]]) { @@ -1678,7 +1678,7 @@ - (void)checkRoomSummary:(Class)mxStoreClass [store2 openWithCredentials:bobRestClient.credentials onComplete:^{ - MXRoomSummary *summary = [store2 summaryOfRoom:response.roomId]; + MXRoomSummary *summary = [store2.roomSummaryStore summaryOfRoom:response.roomId]; XCTAssert(summary); diff --git a/MatrixSDKTests/TestPlans/UnitTests.xctestplan b/MatrixSDKTests/TestPlans/UnitTests.xctestplan index 315e332840..43d3b24624 100644 --- a/MatrixSDKTests/TestPlans/UnitTests.xctestplan +++ b/MatrixSDKTests/TestPlans/UnitTests.xctestplan @@ -35,6 +35,7 @@ "MXAsyncTaskQueueUnitTests", "MXAuthenticationSessionUnitTests", "MXBackgroundTaskUnitTests", + "MXCoreDataRoomListDataManagerUnitTests", "MXCredentialsUnitTests", "MXDeviceListOperationsPoolUnitTests", "MXErrorUnitTests", @@ -51,7 +52,7 @@ "MXQRCodeDataUnitTests", "MXReplyEventParserUnitTests", "MXResponseUnitTests", - "MXRoomListDataManagerUnitTests", + "MXStoreRoomListDataManagerUnitTests", "MXSyncResponseUnitTests", "MXToolsUnitTests" ], diff --git a/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan b/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan index 4401b4259d..ffba5a7c80 100644 --- a/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan +++ b/MatrixSDKTests/TestPlans/UnitTestsWithSanitizers.xctestplan @@ -45,6 +45,7 @@ "MXAsyncTaskQueueUnitTests", "MXAuthenticationSessionUnitTests", "MXBackgroundTaskUnitTests", + "MXCoreDataRoomListDataManagerUnitTests", "MXCredentialsUnitTests", "MXDeviceListOperationsPoolUnitTests", "MXErrorUnitTests", @@ -61,7 +62,7 @@ "MXQRCodeDataUnitTests", "MXReplyEventParserUnitTests", "MXResponseUnitTests", - "MXRoomListDataManagerUnitTests", + "MXStoreRoomListDataManagerUnitTests", "MXSyncResponseUnitTests", "MXToolsUnitTests" ],