diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index e50b13add8..20250ea084 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -622,6 +622,12 @@ 9CC8EFC1457B94FB58DFF1F7 /* libPods-MatrixSDK-MatrixSDK-macOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA84A2BE7767F3645CAEC2AF /* libPods-MatrixSDK-MatrixSDK-macOS.a */; }; A816247C25F60C7700A46F05 /* MXDeviceListOperationsPoolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolTests.swift */; }; A816248525F60D0300A46F05 /* MXDeviceListOperationsPoolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolTests.swift */; }; + B105CD9D261E0B70006EB204 /* MXSpaceChildrenSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = B105CD9C261E0B70006EB204 /* MXSpaceChildrenSummary.swift */; }; + B105CD9E261E0B70006EB204 /* MXSpaceChildrenSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = B105CD9C261E0B70006EB204 /* MXSpaceChildrenSummary.swift */; }; + B105CDD6261F54C8006EB204 /* MXSpaceChildContent.h in Headers */ = {isa = PBXBuildFile; fileRef = B105CDD4261F54C8006EB204 /* MXSpaceChildContent.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 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 */; }; B10AFB4422A970060092E6AF /* MXEventReplace.m in Sources */ = {isa = PBXBuildFile; fileRef = B10AFB4222A970060092E6AF /* MXEventReplace.m */; }; B10AFB4722AA8A8E0092E6AF /* MXEventEditsListener.h in Headers */ = {isa = PBXBuildFile; fileRef = B10AFB4522AA8A8D0092E6AF /* MXEventEditsListener.h */; }; @@ -1097,8 +1103,12 @@ B165B81225C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h in Headers */ = {isa = PBXBuildFile; fileRef = B165B81025C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h */; settings = {ATTRIBUTES = (Public, ); }; }; B1660F1C260A20B900C3AA12 /* MXSpaceServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1660F1B260A20B900C3AA12 /* MXSpaceServiceTest.swift */; }; B1660F1D260A20B900C3AA12 /* MXSpaceServiceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1660F1B260A20B900C3AA12 /* MXSpaceServiceTest.swift */; }; + B16C56E2261D0A9D00604765 /* MXSpaceChildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16C56E1261D0A9D00604765 /* MXSpaceChildInfo.swift */; }; + B16C56E3261D0A9D00604765 /* MXSpaceChildInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16C56E1261D0A9D00604765 /* MXSpaceChildInfo.swift */; }; B16F35A225F916A00029AE98 /* MXRoomTypeMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16F35A125F9169F0029AE98 /* MXRoomTypeMapper.swift */; }; B16F35A325F916A00029AE98 /* MXRoomTypeMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16F35A125F9169F0029AE98 /* MXRoomTypeMapper.swift */; }; + B1710B1F2613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1710B1E2613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift */; }; + B1710B202613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1710B1E2613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift */; }; B17285792100C8EA0052C51E /* MXSendReplyEventStringsLocalizable.h in Headers */ = {isa = PBXBuildFile; fileRef = B17285782100C88A0052C51E /* MXSendReplyEventStringsLocalizable.h */; settings = {ATTRIBUTES = (Public, ); }; }; B172857C2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h in Headers */ = {isa = PBXBuildFile; fileRef = B172857A2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h */; settings = {ATTRIBUTES = (Public, ); }; }; B172857D2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m in Sources */ = {isa = PBXBuildFile; fileRef = B172857B2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m */; }; @@ -1172,6 +1182,14 @@ B19A30D92404335D00FB6F35 /* MXQRCodeDataTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B19A30AE240425D000FB6F35 /* MXQRCodeDataTests.m */; }; B19EC8A2260E134A00543BEC /* MXRoomInitialStateEventBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EC8A1260E134A00543BEC /* MXRoomInitialStateEventBuilder.swift */; }; B19EC8A3260E134A00543BEC /* MXRoomInitialStateEventBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EC8A1260E134A00543BEC /* MXRoomInitialStateEventBuilder.swift */; }; + B1A026F626161EF5001AADFF /* MXSpaceChildSummaryResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = B1A026F426161EF5001AADFF /* MXSpaceChildSummaryResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1A026F726161EF5001AADFF /* MXSpaceChildSummaryResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = B1A026F426161EF5001AADFF /* MXSpaceChildSummaryResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1A026F826161EF5001AADFF /* MXSpaceChildSummaryResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = B1A026F526161EF5001AADFF /* MXSpaceChildSummaryResponse.m */; }; + B1A026F926161EF5001AADFF /* MXSpaceChildSummaryResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = B1A026F526161EF5001AADFF /* MXSpaceChildSummaryResponse.m */; }; + B1A0270026162110001AADFF /* MXSpaceChildrenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = B1A026FE26162110001AADFF /* MXSpaceChildrenResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1A0270126162110001AADFF /* MXSpaceChildrenResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = B1A026FE26162110001AADFF /* MXSpaceChildrenResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B1A0270226162110001AADFF /* MXSpaceChildrenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = B1A026FF26162110001AADFF /* MXSpaceChildrenResponse.m */; }; + B1A0270326162110001AADFF /* MXSpaceChildrenResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = B1A026FF26162110001AADFF /* MXSpaceChildrenResponse.m */; }; B1C854EE25E7B497005867D0 /* MXRoomType.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C854ED25E7B492005867D0 /* MXRoomType.h */; settings = {ATTRIBUTES = (Public, ); }; }; B1C854EF25E7B498005867D0 /* MXRoomType.h in Headers */ = {isa = PBXBuildFile; fileRef = B1C854ED25E7B492005867D0 /* MXRoomType.h */; settings = {ATTRIBUTES = (Public, ); }; }; B1DDC9D62418098200D208E3 /* MXIncomingSASTransaction_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1224,6 +1242,8 @@ B1E09A462397FD990057C069 /* MXMediaScanStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4FD21A5C0BC00D8C2C6 /* MXMediaScanStoreTests.m */; }; B1E09A472397FD990057C069 /* MXEventScanStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = B146D4FC21A5C0BC00D8C2C6 /* MXEventScanStoreTests.m */; }; B1E09A492398028D0057C069 /* MXSelfSignedHomeserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32322A471E57264E005DD155 /* MXSelfSignedHomeserverTests.m */; }; + B1F939F526289F2600D0E525 /* MXSpaceChildContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F939F426289F2600D0E525 /* MXSpaceChildContentTests.swift */; }; + B1F939F626289F2600D0E525 /* MXSpaceChildContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F939F426289F2600D0E525 /* MXSpaceChildContentTests.swift */; }; C60165381E3AA57900B92CFA /* MXSDKOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = F0C34CB91C18C80000C36F09 /* MXSDKOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; C602B58C1F2268F700B67D87 /* MXRoom.swift in Sources */ = {isa = PBXBuildFile; fileRef = C602B58B1F2268F700B67D87 /* MXRoom.swift */; }; C602B58E1F22A8D700B67D87 /* MXImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C602B58D1F22A8D700B67D87 /* MXImage.swift */; }; @@ -1918,6 +1938,9 @@ 9274AFE61EE580240009BEB6 /* MXCallKitAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXCallKitAdapter.h; sourceTree = ""; }; 9274AFE71EE580240009BEB6 /* MXCallKitAdapter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXCallKitAdapter.m; sourceTree = ""; }; A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXDeviceListOperationsPoolTests.swift; sourceTree = ""; }; + B105CD9C261E0B70006EB204 /* MXSpaceChildrenSummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSpaceChildrenSummary.swift; sourceTree = ""; }; + B105CDD4261F54C8006EB204 /* MXSpaceChildContent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSpaceChildContent.h; sourceTree = ""; }; + B105CDD5261F54C8006EB204 /* MXSpaceChildContent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSpaceChildContent.m; sourceTree = ""; }; B10AFB4122A970060092E6AF /* MXEventReplace.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXEventReplace.h; sourceTree = ""; }; B10AFB4222A970060092E6AF /* MXEventReplace.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXEventReplace.m; sourceTree = ""; }; B10AFB4522AA8A8D0092E6AF /* MXEventEditsListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXEventEditsListener.h; sourceTree = ""; }; @@ -1984,7 +2007,9 @@ B14EF36B2397E90400758AF0 /* MatrixSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MatrixSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B165B81025C3307E003CF7F7 /* MXLoginSSOIdentityProviderBrand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLoginSSOIdentityProviderBrand.h; sourceTree = ""; }; B1660F1B260A20B900C3AA12 /* MXSpaceServiceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSpaceServiceTest.swift; sourceTree = ""; }; + B16C56E1261D0A9D00604765 /* MXSpaceChildInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSpaceChildInfo.swift; sourceTree = ""; }; B16F35A125F9169F0029AE98 /* MXRoomTypeMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomTypeMapper.swift; sourceTree = ""; }; + B1710B1E2613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSpaceChildrenRequestParameters.swift; sourceTree = ""; }; B17285782100C88A0052C51E /* MXSendReplyEventStringsLocalizable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MXSendReplyEventStringsLocalizable.h; path = ../MXSendReplyEventStringsLocalizable.h; sourceTree = ""; }; B172857A2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MXSendReplyEventDefaultStringLocalizations.h; path = ../MXSendReplyEventDefaultStringLocalizations.h; sourceTree = ""; }; B172857B2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = MXSendReplyEventDefaultStringLocalizations.m; path = ../MXSendReplyEventDefaultStringLocalizations.m; sourceTree = ""; }; @@ -2030,9 +2055,14 @@ B19A30D224042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSelfVerifyingMasterKeyNotTrustedQRCodeData.h; sourceTree = ""; }; B19A30D324042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSelfVerifyingMasterKeyNotTrustedQRCodeData.m; sourceTree = ""; }; B19EC8A1260E134A00543BEC /* MXRoomInitialStateEventBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXRoomInitialStateEventBuilder.swift; sourceTree = ""; }; + B1A026F426161EF5001AADFF /* MXSpaceChildSummaryResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSpaceChildSummaryResponse.h; sourceTree = ""; }; + B1A026F526161EF5001AADFF /* MXSpaceChildSummaryResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSpaceChildSummaryResponse.m; sourceTree = ""; }; + B1A026FE26162110001AADFF /* MXSpaceChildrenResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXSpaceChildrenResponse.h; sourceTree = ""; }; + B1A026FF26162110001AADFF /* MXSpaceChildrenResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXSpaceChildrenResponse.m; sourceTree = ""; }; B1C854ED25E7B492005867D0 /* MXRoomType.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXRoomType.h; sourceTree = ""; }; B1DDC9D52418098200D208E3 /* MXIncomingSASTransaction_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXIncomingSASTransaction_Private.h; sourceTree = ""; }; B1E09A0E2397FA950057C069 /* MatrixSDKTests-macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "MatrixSDKTests-macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + B1F939F426289F2600D0E525 /* MXSpaceChildContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MXSpaceChildContentTests.swift; sourceTree = ""; }; B57EF0A39A7649D55CA1208A /* libPods-MatrixSDK-MatrixSDK-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MatrixSDK-MatrixSDK-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; BD0431D223F108AED4C70A42 /* Pods-SDK-MatrixSDK-iOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDK-MatrixSDK-iOS.debug.xcconfig"; path = "Target Support Files/Pods-SDK-MatrixSDK-iOS/Pods-SDK-MatrixSDK-iOS.debug.xcconfig"; sourceTree = ""; }; BD96770F88F116285272DB47 /* Pods-MatrixSDKTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixSDKTests.release.xcconfig"; path = "Target Support Files/Pods-MatrixSDKTests/Pods-MatrixSDKTests.release.xcconfig"; sourceTree = ""; }; @@ -3209,6 +3239,7 @@ A816247B25F60C7700A46F05 /* MXDeviceListOperationsPoolTests.swift */, EC383BC12542F251002FBBE6 /* MatrixSDKTests-Bridging-Header.h */, B1660F1B260A20B900C3AA12 /* MXSpaceServiceTest.swift */, + B1F939F426289F2600D0E525 /* MXSpaceChildContentTests.swift */, ); path = MatrixSDKTests; sourceTree = ""; @@ -3551,6 +3582,15 @@ B18B0E6625FBDC2F00E32151 /* MXSpace.swift */, B18B0E4325FB73C300E32151 /* MXSpaceService.swift */, B18B0E4925FB783B00E32151 /* MXSpaceCreationParameters.swift */, + B1710B1E2613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift */, + B1A026FE26162110001AADFF /* MXSpaceChildrenResponse.h */, + B1A026FF26162110001AADFF /* MXSpaceChildrenResponse.m */, + B1A026F426161EF5001AADFF /* MXSpaceChildSummaryResponse.h */, + B1A026F526161EF5001AADFF /* MXSpaceChildSummaryResponse.m */, + B16C56E1261D0A9D00604765 /* MXSpaceChildInfo.swift */, + B105CD9C261E0B70006EB204 /* MXSpaceChildrenSummary.swift */, + B105CDD4261F54C8006EB204 /* MXSpaceChildContent.h */, + B105CDD5261F54C8006EB204 /* MXSpaceChildContent.m */, ); path = Space; sourceTree = ""; @@ -3822,6 +3862,7 @@ 3281E8B719E42DFE00976E1A /* MXJSONModel.h in Headers */, EC8A539325B1BC77004E0802 /* MXCallSessionDescription.h in Headers */, 327E9AF62289D53800A98BC1 /* MXReactionCount.h in Headers */, + B1A0270026162110001AADFF /* MXSpaceChildrenResponse.h in Headers */, 320B393A239FD15E00BE2C06 /* MXKeyVerificationRequest.h in Headers */, B146D4AF21A5A04300D8C2C6 /* MXMediaScanStore.h in Headers */, 32A1514A1DAF7C0C00400192 /* MXUsersDevicesMap.h in Headers */, @@ -3910,6 +3951,7 @@ EC8A53C325B1BC77004E0802 /* MXCallInviteEventContent.h in Headers */, 3281E8B919E42DFE00976E1A /* MXJSONModels.h in Headers */, 3A108AA225810FE5005EEBE9 /* MXRawDataKey.h in Headers */, + B1A026F626161EF5001AADFF /* MXSpaceChildSummaryResponse.h in Headers */, EC05478225FF99450047ECD7 /* MXRoomAccountDataUpdater.h in Headers */, 8EC511042568216B00EC4E5B /* MXTaggedEventInfo.h in Headers */, B19A30D424042F2700FB6F35 /* MXSelfVerifyingMasterKeyNotTrustedQRCodeData.h in Headers */, @@ -4061,6 +4103,7 @@ 327E37B61A974F75007F026F /* MXLogger.h in Headers */, 320A883C217F4E35002EA952 /* MXMegolmBackupCreationInfo.h in Headers */, B19A30AA2404257700FB6F35 /* MXQRCodeKeyVerificationStart.h in Headers */, + B105CDD6261F54C8006EB204 /* MXSpaceChildContent.h in Headers */, 32F00ABB2488E1CD00131741 /* MXRecoveryService.h in Headers */, 3256E3811DCB91EB003C9718 /* MXCryptoConstants.h in Headers */, 320DFDE619DD99B60068622A /* MXHTTPClient.h in Headers */, @@ -4087,6 +4130,7 @@ B14EF30E2397E90400758AF0 /* MXPushRuleRoomMemberCountConditionChecker.h in Headers */, B14EF3182397E90400758AF0 /* MXPushRuleSenderNotificationPermissionConditionChecker.h in Headers */, B14EF3272397E90400758AF0 /* MXPushRuleEventMatchConditionChecker.h in Headers */, + B1A0270126162110001AADFF /* MXSpaceChildrenResponse.h in Headers */, B14EF2E32397E90400758AF0 /* MXDecrypting.h in Headers */, B14EF3062397E90400758AF0 /* MXEncrypting.h in Headers */, B14EF2972397E90400758AF0 /* MXCrypto.h in Headers */, @@ -4205,6 +4249,7 @@ B14EF2E62397E90400758AF0 /* MXMegolmEncryption.h in Headers */, EC8A53E125B1BCC6004E0802 /* MXThirdPartyProtocolInstance.h in Headers */, B14EF2E72397E90400758AF0 /* MXLRUCache.h in Headers */, + B105CDD7261F54C8006EB204 /* MXSpaceChildContent.h in Headers */, 32AF929824115D8B0008A0FD /* MXPendingSecretShareRequest.h in Headers */, B14EF2E82397E90400758AF0 /* MXSendReplyEventDefaultStringLocalizations.h in Headers */, B14EF2E92397E90400758AF0 /* MXTools.h in Headers */, @@ -4240,6 +4285,7 @@ 32C78B69256CFC4D008130B1 /* MXCryptoVersion.h in Headers */, B14EF2FF2397E90400758AF0 /* MXSendReplyEventStringsLocalizable.h in Headers */, EC8A53BC25B1BC77004E0802 /* MXCallSelectAnswerEventContent.h in Headers */, + B1A026F726161EF5001AADFF /* MXSpaceChildSummaryResponse.h in Headers */, B14EF3002397E90400758AF0 /* MXQueuedEncryption.h in Headers */, 324AAC822399143400380A66 /* MXKeyVerificationMac.h in Headers */, B14EF3012397E90400758AF0 /* MXIncomingRoomKeyRequestManager.h in Headers */, @@ -4653,6 +4699,7 @@ 322360531A8E610500A3CA81 /* MXPushRuleDisplayNameCondtionChecker.m in Sources */, 32A31BC520D3FFB0005916C7 /* MXFilter.m in Sources */, 32B76EA520FDE85100B095F6 /* MXRoomMembersCount.m in Sources */, + B1710B1F2613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift in Sources */, 32CEEF4B23B0A8170039BA98 /* MXCrossSigningTools.m in Sources */, EC8A53A525B1BC77004E0802 /* MXCallInviteEventContent.m in Sources */, 323F877D25546170009E9E67 /* MXBaseProfiler.m in Sources */, @@ -4666,6 +4713,7 @@ 32999DE022DCD183004FF987 /* MXPusher.m in Sources */, F03EF4FF1DF014D9009DF592 /* MXMediaLoader.m in Sources */, 320A8841217F4E3F002EA952 /* MXMegolmBackupAuthData.m in Sources */, + B1A0270226162110001AADFF /* MXSpaceChildrenResponse.m in Sources */, 327E9AF72289D53800A98BC1 /* MXReactionCount.m in Sources */, 32FFB4F1217E146A00C96002 /* MXRecoveryKey.m in Sources */, 32DC15D51A8CF874006F9AD3 /* MXPushRuleEventMatchConditionChecker.m in Sources */, @@ -4723,6 +4771,7 @@ 326056861C76FDF2009D44AD /* MXEventTimeline.m in Sources */, 327A5F4F239805F600ED6329 /* MXKeyVerificationAccept.m in Sources */, B19A30BA2404268600FB6F35 /* MXQRCodeData.m in Sources */, + B105CDD8261F54C8006EB204 /* MXSpaceChildContent.m in Sources */, 327E9ABD2284521C00A98BC1 /* MXEventUnsignedData.m in Sources */, 32AF9286240EA2430008A0FD /* MXSecretShareRequest.m in Sources */, 32A1513A1DAD292400400192 /* MXMegolmEncryption.m in Sources */, @@ -4763,6 +4812,7 @@ EC8A53C525B1BC77004E0802 /* MXTurnServerResponse.m in Sources */, 32A151271DABB0CB00400192 /* MXMegolmDecryption.m in Sources */, 327A5F50239805F600ED6329 /* MXKeyVerificationKey.m in Sources */, + B16C56E2261D0A9D00604765 /* MXSpaceChildInfo.swift in Sources */, 321CFDFE2254E8C4004D31DF /* MXEmojiRepresentation.m in Sources */, B10AFB4822AA8A8E0092E6AF /* MXEventEditsListener.m in Sources */, C6D5D60E1E4FBD2900706C0F /* MXSession.swift in Sources */, @@ -4837,6 +4887,7 @@ F03EF5091DF071D5009DF592 /* MXEncryptedAttachments.m in Sources */, 324DD2C1246D658500377005 /* MXHkdfSha256.m in Sources */, 3274538623FD69D600438328 /* MXKeyVerificationRequestByToDeviceJSONModel.m in Sources */, + B1A026F826161EF5001AADFF /* MXSpaceChildSummaryResponse.m in Sources */, 324DD2C7246E638B00377005 /* MXAesHmacSha2.m in Sources */, B172857D2100D4F60052C51E /* MXSendReplyEventDefaultStringLocalizations.m in Sources */, 321CFDEF225264C4004D31DF /* NSArray+MatrixSDK.m in Sources */, @@ -4864,6 +4915,7 @@ 320DFDE119DD99B60068622A /* MXSession.m in Sources */, B17982F62119E4A2001FD722 /* MXRoomTombStoneContent.m in Sources */, C602B58E1F22A8D700B67D87 /* MXImage.swift in Sources */, + B105CD9D261E0B70006EB204 /* MXSpaceChildrenSummary.swift in Sources */, 32A9F8E0244720B10069C65B /* MXThrottler.m in Sources */, EC8A53E425B1BCC6004E0802 /* MXThirdPartyUsersResponse.m in Sources */, 3295401A216385F100E300FC /* MXServerNoticeContent.m in Sources */, @@ -4994,6 +5046,7 @@ 32935F61216FA49D00A1BC24 /* MXCryptoBackupTests.m in Sources */, 328DDEC11A07E57E008C7DC8 /* MXJSONModelTests.m in Sources */, 32832B5C1BCC048300241108 /* MXStoreFileStoreTests.m in Sources */, + B1F939F526289F2600D0E525 /* MXSpaceChildContentTests.swift in Sources */, 320E1BC11E0AD674009635F5 /* MXRoomSummaryTests.m in Sources */, 3281E8A219E2DE4300976E1A /* MXSessionTests.m in Sources */, 327137241A24BDDE00DB6757 /* MXUserTests.m in Sources */, @@ -5039,6 +5092,7 @@ B14EF1D32397E90400758AF0 /* MXRoomMembersCount.m in Sources */, B14EF1D42397E90400758AF0 /* MXRealmHelper.m in Sources */, B19A30C12404268600FB6F35 /* MXQRCodeDataCoder.m in Sources */, + B16C56E3261D0A9D00604765 /* MXSpaceChildInfo.swift in Sources */, B14EF1D52397E90400758AF0 /* MXEventAnnotation.m in Sources */, B14EF1D62397E90400758AF0 /* MXPusher.m in Sources */, B14EF1D72397E90400758AF0 /* MXMediaLoader.m in Sources */, @@ -5230,6 +5284,7 @@ 324AAC752399140D00380A66 /* MXKeyVerificationAccept.m in Sources */, B14EF2542397E90400758AF0 /* MXReceiptData.m in Sources */, 32B0E34223A378320054FF1A /* MXEventReference.m in Sources */, + B105CD9E261E0B70006EB204 /* MXSpaceChildrenSummary.swift in Sources */, B14EF2552397E90400758AF0 /* MXWellKnownBaseConfig.m in Sources */, B14EF2562397E90400758AF0 /* MXLogger.m in Sources */, EC383BB425406894002FBBE6 /* MXSyncResponseFileStore.swift in Sources */, @@ -5249,6 +5304,7 @@ B14EF2612397E90400758AF0 /* MXRealmAggregationsStore.m in Sources */, EC383BB72541C518002FBBE6 /* MXBackgroundPushRulesManager.swift in Sources */, 3259CFE726026A6F00C365DB /* MXRestClient+Extensions.swift in Sources */, + B105CDD9261F54C8006EB204 /* MXSpaceChildContent.m in Sources */, B14EF2622397E90400758AF0 /* MXAntivirusScanStatusFormatter.m in Sources */, 324AAC7C2399140D00380A66 /* MXKeyVerificationStart.m in Sources */, B14EF2632397E90400758AF0 /* MXReactionCountChange.m in Sources */, @@ -5288,6 +5344,7 @@ B14EF2772397E90400758AF0 /* MXDecryptionResult.m in Sources */, B14EF2782397E90400758AF0 /* MXTransactionCancelCode.m in Sources */, B14EF2792397E90400758AF0 /* MXEventListener.m in Sources */, + B1710B202613D01400A9B429 /* MXSpaceChildrenRequestParameters.swift in Sources */, B14EF27A2397E90400758AF0 /* MXSessionEventListener.swift in Sources */, B14EF27B2397E90400758AF0 /* MXOlmSessionResult.m in Sources */, 323F877E25546170009E9E67 /* MXBaseProfiler.m in Sources */, @@ -5325,6 +5382,7 @@ B14EF28D2397E90400758AF0 /* MXMegolmSessionData.m in Sources */, 3259D02626037A7200C365DB /* NSDictionary.swift in Sources */, EC8A53BE25B1BC77004E0802 /* MXCallRejectEventContent.m in Sources */, + B1A0270326162110001AADFF /* MXSpaceChildrenResponse.m in Sources */, B14EF28E2397E90400758AF0 /* MXKeyVerificationTransaction.m in Sources */, B14EF28F2397E90400758AF0 /* MXEventTimeline.swift in Sources */, B14EF2902397E90400758AF0 /* MXJSONModels.swift in Sources */, @@ -5332,6 +5390,7 @@ EC8A53A225B1BC77004E0802 /* MXCallSelectAnswerEventContent.m in Sources */, B14EF2912397E90400758AF0 /* MXOutgoingRoomKeyRequestManager.m in Sources */, B14EF2922397E90400758AF0 /* MXWellKnown.m in Sources */, + B1A026F926161EF5001AADFF /* MXSpaceChildSummaryResponse.m in Sources */, EC1EFA342567F6570089C01D /* MXSyncResponseStoreModel.m in Sources */, B14EF2932397E90400758AF0 /* MXRestClient.m in Sources */, ); @@ -5385,6 +5444,7 @@ 32EEA84B2603FDD60041425B /* MXResponseTest.swift in Sources */, 32B0E3E823A3864C0054FF1A /* MXEventReferenceTests.swift in Sources */, B1E09A402397FD820057C069 /* MXVoIPTests.m in Sources */, + B1F939F626289F2600D0E525 /* MXSpaceChildContentTests.swift in Sources */, B1E09A412397FD820057C069 /* MXAccountDataTests.m in Sources */, B1E09A2D2397FD750057C069 /* MXRestClientNoAuthAPITests.m in Sources */, B1E09A332397FD750057C069 /* MXRoomStateTests.m in Sources */, diff --git a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift index dc00c1e1af..f7c8afd4ca 100644 --- a/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift +++ b/MatrixSDK/Contrib/Swift/JSONModels/MXEvent.swift @@ -70,6 +70,7 @@ public enum MXEventType: Equatable, Hashable { case keyVerificationCancel case keyVerificationDone case taggedEvents + case spaceChild case custom(String) @@ -116,6 +117,7 @@ public enum MXEventType: Equatable, Hashable { case .keyVerificationCancel: return kMXEventTypeStringKeyVerificationCancel case .keyVerificationDone: return kMXEventTypeStringKeyVerificationDone case .taggedEvents: return kMXEventTypeStringTaggedEvents + case .spaceChild: return kMXEventTypeStringSpaceChild // Swift converts any constant with the suffix "Notification" as the type `Notification.Name` // The original value can be reached using the `rawValue` property. diff --git a/MatrixSDK/Contrib/Swift/MXRestClient.swift b/MatrixSDK/Contrib/Swift/MXRestClient.swift index 15351aab20..0e59851752 100644 --- a/MatrixSDK/Contrib/Swift/MXRestClient.swift +++ b/MatrixSDK/Contrib/Swift/MXRestClient.swift @@ -1862,4 +1862,15 @@ public extension MXRestClient { return __deleteDevice(byDeviceId: deviceId, authParams: authParameters, success: currySuccess(completion), failure: curryFailure(completion)) } + // MARK: - Spaces + + /// Get the space children of a given space. + /// - Parameters: + /// - spaceId: The room id of the queried space. + /// - parameters: Space children request parameters. + /// - completion: A closure called when the operation completes. + /// - Returns: a `MXHTTPOperation` instance. + @nonobjc @discardableResult func getSpaceChildrenForSpace(withId spaceId: String, parameters: MXSpaceChildrenRequestParameters?, completion: @escaping (_ response: MXResponse) -> Void) -> MXHTTPOperation { + return __getSpaceChildrenForSpace(withId: spaceId, parameters: parameters, success: currySuccess(completion), failure: curryFailure(completion)) + } } diff --git a/MatrixSDK/JSONModels/MXEvent.h b/MatrixSDK/JSONModels/MXEvent.h index 2213680d40..4627250265 100644 --- a/MatrixSDK/JSONModels/MXEvent.h +++ b/MatrixSDK/JSONModels/MXEvent.h @@ -88,6 +88,7 @@ typedef NS_ENUM(NSInteger, MXEventType) MXEventTypeSecretSend, MXEventTypeSecretStorageDefaultKey, MXEventTypeTaggedEvents, + MXEventTypeSpaceChild, // The event is a custom event. Refer to its `MXEventTypeString` version MXEventTypeCustom = 1000 @@ -141,6 +142,7 @@ FOUNDATION_EXPORT NSString *const kMXEventTypeStringCallRejectReplacement; FOUNDATION_EXPORT NSString *const kMXEventTypeStringSticker; FOUNDATION_EXPORT NSString *const kMXEventTypeStringRoomTombStone; FOUNDATION_EXPORT NSString *const kMXEventTypeStringTaggedEvents; +FOUNDATION_EXPORT NSString *const kMXEventTypeStringSpaceChild; // Interactive key verification FOUNDATION_EXPORT NSString *const kMXEventTypeStringKeyVerificationRequest; diff --git a/MatrixSDK/JSONModels/MXEvent.m b/MatrixSDK/JSONModels/MXEvent.m index 9d4c9d9d91..e3c230cc40 100644 --- a/MatrixSDK/JSONModels/MXEvent.m +++ b/MatrixSDK/JSONModels/MXEvent.m @@ -79,6 +79,9 @@ NSString *const kMXEventTypeStringSecretSend = @"m.secret.send"; NSString *const kMXEventTypeStringSecretStorageDefaultKey = @"m.secret_storage.default_key"; NSString *const kMXEventTypeStringTaggedEvents = @"m.tagged_events"; +//NSString *const kMXEventTypeStringSpaceChild = @"m.space.child"; +// Use temporary event type until the MSC approval +NSString *const kMXEventTypeStringSpaceChild = @"org.matrix.msc1772.space.child"; NSString *const kMXMessageTypeText = @"m.text"; NSString *const kMXMessageTypeEmote = @"m.emote"; diff --git a/MatrixSDK/MXRestClient.h b/MatrixSDK/MXRestClient.h index 5ae0648897..5780051eae 100644 --- a/MatrixSDK/MXRestClient.h +++ b/MatrixSDK/MXRestClient.h @@ -41,9 +41,11 @@ #import "MXPusher.h" #import "MXRoomCreationParameters.h" #import "MXTurnServerResponse.h" +#import "MXSpaceChildrenResponse.h" @class MXThirdpartyProtocolsResponse; @class MXThirdPartyUsersResponse; +@class MXSpaceChildrenRequestParameters; #pragma mark - Constants definitions /** @@ -2680,4 +2682,17 @@ typedef MXHTTPOperation* (^MXRestClientIdentityServerAccessTokenHandler)(void (^ success:(void (^)(MXAggregationPaginatedResponse *paginatedResponse))success failure:(void (^)(NSError *error))failure; +#pragma mark - Spaces + +/// Get the space children of a given space. +/// @param spaceId The room id of the queried space. +/// @param parameters Space children request parameters. +/// @param success A block object called when the operation succeeds. It provides a `MXSpaceChildrenResponse` object. +/// @param failure A block object called when the operation fails. +/// @return a MXHTTPOperation instance. +- (MXHTTPOperation*)getSpaceChildrenForSpaceWithId:(NSString*)spaceId + parameters:(MXSpaceChildrenRequestParameters*)parameters + success:(void (^)(MXSpaceChildrenResponse *spaceChildrenResponse))success + failure:(void (^)(NSError *error))failure NS_REFINED_FOR_SWIFT; + @end diff --git a/MatrixSDK/MXRestClient.m b/MatrixSDK/MXRestClient.m index d56c5a9427..8d85730daa 100644 --- a/MatrixSDK/MXRestClient.m +++ b/MatrixSDK/MXRestClient.m @@ -29,6 +29,8 @@ #import "MXThirdpartyProtocolsResponse.h" #import "MXThirdPartyUsersResponse.h" +#import "MatrixSDKSwiftHeader.h" + #pragma mark - Constants definitions /** Prefix used in path of home server API requests. @@ -5332,4 +5334,37 @@ - (MXHTTPOperation*)relationsForEvent:(NSString*)eventId }]; } +#pragma mark - Spaces + +- (MXHTTPOperation*)getSpaceChildrenForSpaceWithId:(NSString*)spaceId + parameters:(MXSpaceChildrenRequestParameters*)parameters + success:(void (^)(MXSpaceChildrenResponse *spaceChildrenResponse))success + failure:(void (^)(NSError *error))failure +{ + NSString *path = [NSString stringWithFormat:@"%@/org.matrix.msc2946/rooms/%@/spaces", + kMXAPIPrefixPathUnstable, spaceId]; + + MXWeakify(self); + return [httpClient requestWithMethod:@"POST" + path:path + parameters:[parameters jsonDictionary] ?: @{} + success:^(NSDictionary *JSONResponse) { + MXStrongifyAndReturnIfNil(self); + + if (success) + { + __block MXSpaceChildrenResponse *spaceChildrenResponse; + [self dispatchProcessing:^{ + MXJSONModelSetMXJSONModel(spaceChildrenResponse, MXSpaceChildrenResponse, JSONResponse); + } andCompletion:^{ + success(spaceChildrenResponse); + }]; + } + } + failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self dispatchFailure:error inBlock:failure]; + }]; +} + @end diff --git a/MatrixSDK/MatrixSDK.h b/MatrixSDK/MatrixSDK.h index bf4a37c40b..3eb2a1fd83 100644 --- a/MatrixSDK/MatrixSDK.h +++ b/MatrixSDK/MatrixSDK.h @@ -126,6 +126,8 @@ FOUNDATION_EXPORT NSString *MatrixSDKVersion; #import "MXAesKeyData.h" #import "MXRawDataKey.h" +#import "MXSpaceChildContent.h" + // Bridging to Swift #import "MXCryptoStore.h" #import "MXRealmCryptoStore.h" diff --git a/MatrixSDK/Space/MXSpace.swift b/MatrixSDK/Space/MXSpace.swift index 3d2a804b38..f89c5c47eb 100644 --- a/MatrixSDK/Space/MXSpace.swift +++ b/MatrixSDK/Space/MXSpace.swift @@ -16,6 +16,24 @@ import Foundation +/// MXSpace operation error +public enum MXSpaceError: Int, Error { + case homeserverNameNotFound + case unknown +} + +extension MXSpaceError: CustomNSError { + public static let errorDomain = "org.matrix.sdk.space" + + public var errorCode: Int { + return Int(rawValue) + } + + public var errorUserInfo: [String: Any] { + return [:] + } +} + /// A Matrix space enables to collect rooms together into groups. Such collections of rooms are referred as "spaces" (see https://github.com/matrix-org/matrix-doc/blob/matthew/msc1772/proposals/1772-groups-as-rooms.md). public class MXSpace: NSObject { @@ -24,6 +42,11 @@ public class MXSpace: NSObject { /// The underlying room public let room: MXRoom + /// Shortcut to the room roomId + public var spaceId: String { + return self.room.roomId + } + /// Shortcut to the room summary public var summary: MXRoomSummary? { return self.room.summary @@ -31,8 +54,85 @@ public class MXSpace: NSObject { // MARK: - Setup - init(room: MXRoom) { + public init(room: MXRoom) { self.room = room super.init() } + + // MARK: - Public + + /// Add child space or child room to the current space. + /// - Parameters: + /// - roomId: The room id of the child space or child room. + /// - viaServers: List of candidate servers that can be used to join the space. Children where via is not present are ignored. + /// If nil value is set current homeserver will be used as via server. + /// - order: Is a string which is used to provide a default ordering of siblings in the room list. Orders should be a string of ascii characters in the range \x20 (space) to \x7F (~), and should be less or equal 50 characters. + /// - autoJoin: Allows a space admin to list the sub-spaces and rooms in that space which should be automatically joined by members of that space. + /// - suggested: Indicates that the child should be advertised to members of the space by the client. This could be done by showing them eagerly in the room list. + /// - completion: A closure called when the operation completes. Provides the event id of the event generated on the home server on success. + /// - Returns: a `MXHTTPOperation` instance. + @discardableResult + public func addChild(roomId: String, + viaServers: [String]? = nil, + order: String? = nil, + autoJoin: Bool = false, + suggested: Bool = false, + completion: @escaping (_ response: MXResponse) -> Void) -> MXHTTPOperation? { + + let finalViaServers: [String] + + if let viaServers = viaServers { + finalViaServers = viaServers + } else { + // If viaServers is nil use the current homeserver as via server + guard let homeserverName = self.room.mxSession.credentials.homeServerName() else { + completion(.failure(MXSpaceError.homeserverNameNotFound)) + return nil + } + finalViaServers = [homeserverName] + } + + let spaceChild = MXSpaceChildContent() + spaceChild.via = finalViaServers + spaceChild.order = order + spaceChild.autoJoin = autoJoin + spaceChild.suggested = suggested + + guard let stateEventContent = spaceChild.jsonDictionary() as? [String: Any] else { + fatalError("[MXSpace] MXSpaceChildContent dictionary cannot be nil") + } + + return self.room.sendStateEvent(.spaceChild, + content: stateEventContent, + stateKey: roomId, + completion: completion) + } +} + +// MARK: - Objective-C +extension MXSpace { + + /// Add child space or child room to the current space. + /// - Parameters: + /// - roomId: The room id of the child space or child room. + /// - viaServers: List of candidate servers that can be used to join the space. Children where via is not present are ignored. + /// If nil value is set current homeserver will be used as via server. + /// - order: Is a string which is used to provide a default ordering of siblings in the room list. Orders should be a string of ascii characters in the range \x20 (space) to \x7F (~), and should be less or equal 50 characters. + /// - autoJoin: Allows a space admin to list the sub-spaces and rooms in that space which should be automatically joined by members of that space. + /// - suggested: Indicates that the child should be advertised to members of the space by the client. This could be done by showing them eagerly in the room list. + /// - success: A closure called when the operation is complete. Provides the event id of the event generated on the home server on success. + /// - failure: A closure called when the operation fails. + /// - Returns: a `MXHTTPOperation` instance. + @discardableResult + public func addChild(roomId: String, + viaServers: [String]?, + order: String?, + autoJoin: Bool, + suggested: Bool, + success: @escaping (String?) -> Void, + failure: @escaping (Error) -> Void) -> MXHTTPOperation? { + return self.addChild(roomId: roomId, viaServers: viaServers, order: order, autoJoin: autoJoin, suggested: suggested) { (response) in + uncurryResponse(response, success: success, failure: failure) + } + } } diff --git a/MatrixSDK/Space/MXSpaceChildContent.h b/MatrixSDK/Space/MXSpaceChildContent.h new file mode 100644 index 0000000000..310eb51ac1 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildContent.h @@ -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 +#import "MXJSONModel.h" + +NS_ASSUME_NONNULL_BEGIN + +/// MXSpaceChildContent represents the state event content of space child event type (MXEventType.spaceChild). +@interface MXSpaceChildContent : MXJSONModel + +/// Key which gives a list of candidate servers that can be used to join the room +/// Children where via is not present are ignored. +@property (nonatomic, strong, nullable) NSArray* via; + +/// The order key is a string which is used to provide a default ordering of siblings in the room list. +/// (Rooms are sorted based on a lexicographic ordering of order values; rooms with no order come last. +/// orders which are not strings, or do not consist solely of ascii characters in the range \x20 (space) to \x7F (~), +/// or consist of more than 50 characters, are forbidden and should be ignored if received.) +@property (nonatomic, strong, nullable) NSString *order; + +/// The auto_join flag on a child listing allows a space admin to list the sub-spaces and rooms in that space which should be automatically joined by members of that space. +/// (This is not a force-join, which are descoped for a future MSC; the user can subsequently part these room if they desire.) +/// `NO` by default. +@property (nonatomic) BOOL autoJoin; + +/// If `suggested` is set to `true`, that indicates that the child should be advertised to members of the space by the client. This could be done by showing them eagerly in the room list. +/// This is should be ignored if `auto_join` is set to `true`. +/// `NO` by default. +@property (nonatomic) BOOL suggested; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Space/MXSpaceChildContent.m b/MatrixSDK/Space/MXSpaceChildContent.m new file mode 100644 index 0000000000..2da5b38a88 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildContent.m @@ -0,0 +1,103 @@ +// +// 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 "MXSpaceChildContent.h" + +#pragma mark - Constants + +static NSUInteger const kOrderValueMaxLength = 50; + +// ASCII characters in the range \x20 (space) to \x7F (~) +static NSString* const kOrderTextRegexPattern = @"[ -~]+"; + +@implementation MXSpaceChildContent + +#pragma mark - MXJSONModel + ++ (instancetype)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXSpaceChildContent *spaceChildContent = [MXSpaceChildContent new]; + + if (spaceChildContent) + { + NSString *parsedOrder; + + MXJSONModelSetString(parsedOrder, JSONDictionary[@"order"]); + + if ([self isOrderValid:parsedOrder]) + { + spaceChildContent.order = parsedOrder; + } + + MXJSONModelSetString(spaceChildContent.via, JSONDictionary[@"via"]); + MXJSONModelSetBoolean(spaceChildContent.autoJoin, JSONDictionary[@"auto_join"]) + MXJSONModelSetBoolean(spaceChildContent.suggested, JSONDictionary[@"suggested"]); + } + + return spaceChildContent; +} + +- (NSDictionary *)JSONDictionary +{ + NSMutableDictionary *JSONDictionary = [NSMutableDictionary dictionary]; + + if ([[self class] isOrderValid:self.order]) + { + JSONDictionary[@"order"] = self.order; + } + else + { + NSLog(@"[MXSpaceChildContent] JSONDictionary: order is not valid"); + } + + if (self.via) + { + JSONDictionary[@"via"] = self.via; + } + + JSONDictionary[@"auto_join"] = @(self.autoJoin); + JSONDictionary[@"suggested"] = @(self.suggested); + + return JSONDictionary; +} + +#pragma mark - Private + +/// Orders which are not strings, or do not consist solely of ascii characters in the range \x20 (space) to \x7F (~), +/// or consist of more than 50 characters, are forbidden and should be ignored if received.) ++ (BOOL)isOrderValid:(NSString*)order +{ + if (order.length == 0) + { + return YES; + } + + if (order.length > kOrderValueMaxLength) + { + return NO; + } + + static NSPredicate *predicate; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + predicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", kOrderTextRegexPattern]; + }); + + return [predicate evaluateWithObject:order]; +} + +@end diff --git a/MatrixSDK/Space/MXSpaceChildInfo.swift b/MatrixSDK/Space/MXSpaceChildInfo.swift new file mode 100644 index 0000000000..b0a5654970 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildInfo.swift @@ -0,0 +1,90 @@ +// +// 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 + +/// MXSpaceChildInfo represents space child summary informations. +@objcMembers +public class MXSpaceChildInfo: NSObject { + + // MARK: - Properties + + /// The room id of the child + public var childRoomId: String + + /// True to indicate that the space is known. + /// We might not know this child at all, i.e we just know it exists but no info on type/name/etc.. + public let isKnown: Bool + + /// The room type string value as provided by the server. Can be nil. + public let roomTypeString: String? + + /// The locally computed room type derivated from `roomTypeString`. + public let roomType: MXRoomType + + /// The space name. + public let name: String? + + /// The space topic. + public let topic: String? + + /// The Matrix content URI of the space avatar. + public let avatarUrl: String? + + /// The order key is a string which is used to provide a default ordering of siblings in the room list. + /// Orders should be a string of ascii characters in the range \x20 (space) to \x7F (~), and should be less or equal 50 characters. + public let order: String? + + /// The number of members joined to the room. + public let activeMemberCount: Int + + /// Allows a space admin to list the sub-spaces and rooms in that space which should be automatically joined by members of that space. + public let autoJoin: Bool + + /// Gives a list of candidate servers that can be used to join the space. + public let viaServers: [String] + + /// The parent space room id. + public let parentRoomId: String? + + // MARK: - Setup + + public init(childRoomId: String, + isKnown: Bool, + roomTypeString: String?, + roomType: MXRoomType, + name: String?, + topic: String?, + avatarUrl: String?, + order: String?, + activeMemberCount: Int, + autoJoin: Bool, + viaServers: [String], + parentRoomId: String?) { + self.childRoomId = childRoomId + self.isKnown = isKnown + self.roomTypeString = roomTypeString + self.roomType = roomType + self.name = name + self.topic = topic + self.avatarUrl = avatarUrl + self.order = order + self.activeMemberCount = activeMemberCount + self.autoJoin = autoJoin + self.viaServers = viaServers + self.parentRoomId = parentRoomId + } +} diff --git a/MatrixSDK/Space/MXSpaceChildSummaryResponse.h b/MatrixSDK/Space/MXSpaceChildSummaryResponse.h new file mode 100644 index 0000000000..8aea1f78f7 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildSummaryResponse.h @@ -0,0 +1,59 @@ +// +// 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 "MXJSONModel.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Space child summary +@interface MXSpaceChildSummaryResponse : MXJSONModel + +/// The ID of the room. +@property (nonatomic) NSString* roomId; + +/// The room type, which is m.space for subspaces. +/// It can be omitted if there is no room type in which case it should be interpreted as a normal room. +@property (nonatomic, nullable) NSString* roomType; + +/// The name of the room, if any. +@property (nonatomic, nullable) NSString* name; + +/// The topic of the room, if any. +@property (nonatomic, nullable) NSString* topic; + +/// The URL for the room's avatar, if one is set. +@property (nonatomic, nullable) NSString* avatarUrl; + +/// Aliases of the room. May be empty. +@property (nonatomic, nullable) NSArray* aliases; + +/// The canonical alias of the room, if any. +@property (nonatomic, nullable) NSString* canonicalAlias; + +/// Whether guest users may join the room and participate in it. If they can, +/// they will be subject to ordinary power level rules like any other user. +@property (nonatomic, getter = areGuestCanJoin) BOOL guestCanJoin; + +/// Whether the room may be viewed by guest users without joining. +@property (nonatomic, getter = isWorldReadable) BOOL worldReadable; + +/// The number of members joined to the room. +@property (nonatomic) NSInteger numJoinedMembers; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Space/MXSpaceChildSummaryResponse.m b/MatrixSDK/Space/MXSpaceChildSummaryResponse.m new file mode 100644 index 0000000000..6b2551fbb6 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildSummaryResponse.m @@ -0,0 +1,53 @@ +// +// 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 "MXSpaceChildSummaryResponse.h" + +@implementation MXSpaceChildSummaryResponse + ++ (instancetype)modelFromJSON:(NSDictionary *)JSONDictionary +{ + NSString *roomId; + + MXJSONModelSetString(roomId, JSONDictionary[@"room_id"]); + + // roomId is mandatory + if (!roomId) + { + return nil; + } + + MXSpaceChildSummaryResponse *spaceChildSummaryResponse = [MXSpaceChildSummaryResponse new]; + + if (spaceChildSummaryResponse) + { + spaceChildSummaryResponse.roomId = roomId; + + MXJSONModelSetString(spaceChildSummaryResponse.roomType, JSONDictionary[@"room_type"]); + MXJSONModelSetString(spaceChildSummaryResponse.name, JSONDictionary[@"name"]); + MXJSONModelSetString(spaceChildSummaryResponse.topic, JSONDictionary[@"topic"]); + MXJSONModelSetString(spaceChildSummaryResponse.avatarUrl, JSONDictionary[@"avatar_url"]); + MXJSONModelSetArray(spaceChildSummaryResponse.aliases, JSONDictionary[@"aliases"]); + MXJSONModelSetString(spaceChildSummaryResponse.canonicalAlias, JSONDictionary[@"canonical_alias"]); + MXJSONModelSetBoolean(spaceChildSummaryResponse.guestCanJoin, JSONDictionary[@"guest_can_join"]); + MXJSONModelSetBoolean(spaceChildSummaryResponse.worldReadable, JSONDictionary[@"world_readable"]); + MXJSONModelSetInteger(spaceChildSummaryResponse.numJoinedMembers, JSONDictionary[@"num_joined_members"]); + } + + return spaceChildSummaryResponse; +} + +@end diff --git a/MatrixSDK/Space/MXSpaceChildrenRequestParameters.swift b/MatrixSDK/Space/MXSpaceChildrenRequestParameters.swift new file mode 100644 index 0000000000..6b330b89b4 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildrenRequestParameters.swift @@ -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 Foundation + +/// Space children request parameters +@objcMembers +public class MXSpaceChildrenRequestParameters: NSObject { + + // MARK: - Properties + + /// The maximum number of rooms/subspaces to return for a given space, if negative unbounded. default: -1 + public var maxNumberOfRooms: Int = -1 + + /// The maximum number of rooms/subspaces to return, server can override this, default: 100 + public var limit: Int = -1 + + /// The token returned in the previous response. + public var nextBatch: String? + + /// Optional. If true, return only child events and rooms where the org.matrix.msc1772.space.child event has suggested: true. + public var suggestedRoomOnly: Bool = false + + // MARK: - Public + + public func jsonDictionary() -> [AnyHashable: Any] { + var jsonDictionary: [AnyHashable: Any] = [:] + + if maxNumberOfRooms >= 0 { + jsonDictionary["max_rooms_per_space"] = maxNumberOfRooms + } + + if limit >= 0 { + jsonDictionary["limit"] = limit + } + + if let nextBatch = nextBatch { + jsonDictionary["batch"] = nextBatch + } + + jsonDictionary["suggested_only"] = suggestedRoomOnly + + return jsonDictionary + } +} diff --git a/MatrixSDK/Space/MXSpaceChildrenResponse.h b/MatrixSDK/Space/MXSpaceChildrenResponse.h new file mode 100644 index 0000000000..a381bffd20 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildrenResponse.h @@ -0,0 +1,38 @@ +// +// 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 "MXJSONModel.h" +#import "MXSpaceChildSummaryResponse.h" +#import "MXEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +/// Space API response +@interface MXSpaceChildrenResponse : MXJSONModel + +/// Its presence indicates that there are more results to return. +@property (nonatomic, nullable) NSString* nextBatch; + +/// Rooms information like name/avatar/type ... +@property (nonatomic, nullable) NSArray* rooms; + +/// These are the edges of the graph. The objects in the array are complete (or stripped?) m.room.parent or m.space.child events. +@property (nonatomic, nullable) NSArray* events; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Space/MXSpaceChildrenResponse.m b/MatrixSDK/Space/MXSpaceChildrenResponse.m new file mode 100644 index 0000000000..763e7b7e88 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildrenResponse.m @@ -0,0 +1,35 @@ +// +// 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 "MXSpaceChildrenResponse.h" + +@implementation MXSpaceChildrenResponse + ++ (id)modelFromJSON:(NSDictionary *)JSONDictionary +{ + MXSpaceChildrenResponse *spaceChildrenResponse = [MXSpaceChildrenResponse new]; + + if (spaceChildrenResponse) + { + MXJSONModelSetString(spaceChildrenResponse.nextBatch, JSONDictionary[@"next_batch"]); + MXJSONModelSetMXJSONModelArray(spaceChildrenResponse.rooms, MXSpaceChildSummaryResponse, JSONDictionary[@"rooms"]); + MXJSONModelSetMXJSONModelArray(spaceChildrenResponse.events, MXEvent, JSONDictionary[@"events"]); + } + + return spaceChildrenResponse; +} + +@end diff --git a/MatrixSDK/Space/MXSpaceChildrenSummary.swift b/MatrixSDK/Space/MXSpaceChildrenSummary.swift new file mode 100644 index 0000000000..63d2d4de09 --- /dev/null +++ b/MatrixSDK/Space/MXSpaceChildrenSummary.swift @@ -0,0 +1,37 @@ +// +// 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 + +/// MXSpaceChildrenSummary represents the wrapped result of the space API from MXSpaceService. +@objcMembers +public class MXSpaceChildrenSummary: NSObject { + + // MARK - Properties + + /// The queried space room summary + public let spaceSummary: MXRoomSummary + + /// The child summaries of the queried space + public let childInfos: [MXSpaceChildInfo] + + // MARK - Setup + + init(spaceSummary: MXRoomSummary, childInfos: [MXSpaceChildInfo]) { + self.spaceSummary = spaceSummary + self.childInfos = childInfos + } +} diff --git a/MatrixSDK/Space/MXSpaceService.swift b/MatrixSDK/Space/MXSpaceService.swift index d84f5d4414..4a0624c47c 100644 --- a/MatrixSDK/Space/MXSpaceService.swift +++ b/MatrixSDK/Space/MXSpaceService.swift @@ -16,6 +16,24 @@ import Foundation +/// MXSpaceService error +public enum MXSpaceServiceError: Int, Error { + case spaceNotFound + case unknown +} + +extension MXSpaceServiceError: CustomNSError { + public static let errorDomain = "org.matrix.sdk.spaceService" + + public var errorCode: Int { + return Int(rawValue) + } + + public var errorUserInfo: [String: Any] { + return [:] + } +} + /// MXSpaceService enables to handle spaces. @objcMembers public class MXSpaceService: NSObject { @@ -28,10 +46,18 @@ public class MXSpaceService: NSObject { return MXRoomInitialStateEventBuilder() }() + private let roomTypeMapper: MXRoomTypeMapper + + private let processingQueue: DispatchQueue + private let completionQueue: DispatchQueue + // MARK: - Setup public init(session: MXSession) { self.session = session + self.roomTypeMapper = MXRoomTypeMapper(defaultRoomType: .room) + self.processingQueue = DispatchQueue(label: "org.matrix.sdk.MXSpaceService.processingQueue", attributes: .concurrent) + self.completionQueue = DispatchQueue.main } // MARK: - Public @@ -62,7 +88,7 @@ public class MXSpaceService: NSObject { /// - completion: A closure called when the operation completes. /// - Returns: a `MXHTTPOperation` instance. @discardableResult - public func createSpace(withName name: String, topic: String, isPublic: Bool, completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation { + public func createSpace(withName name: String, topic: String?, isPublic: Bool, completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation { let parameters = MXSpaceCreationParameters() parameters.name = name parameters.topic = topic @@ -87,6 +113,134 @@ public class MXSpaceService: NSObject { let room = self.session.room(withRoomId: spaceId) return room?.toSpace() } + + /// Get the space children informations of a given space from the server. + /// - Parameters: + /// - spaceId: The room id of the queried space. + /// - parameters: Space children request parameters. + /// - completion: A closure called when the operation completes. + /// - Returns: a `MXHTTPOperation` instance. + @discardableResult + public func getSpaceChildrenForSpace(withId spaceId: String, + parameters: MXSpaceChildrenRequestParameters?, + completion: @escaping (MXResponse) -> Void) -> MXHTTPOperation { + return self.session.matrixRestClient.getSpaceChildrenForSpace(withId: spaceId, parameters: parameters) { (response) in + switch response { + case .success(let spaceChildrenResponse): + self.processingQueue.async { [weak self] in + guard let self = self else { + return + } + + guard let rooms = spaceChildrenResponse.rooms else { + // We should have at least one room for the requested space + self.completionQueue.async { + completion(.failure(MXSpaceServiceError.spaceNotFound)) + } + return + } + + guard let rootSpaceChildSummaryResponse = rooms.first(where: { spaceResponse -> Bool in + return spaceResponse.roomId == spaceId + }) else { + // Fail to find root child. We should have at least one room for the requested space + self.completionQueue.async { + completion(.failure(MXSpaceServiceError.spaceNotFound)) + } + return + } + + // Build the queried space summary + let spaceSummary = self.createRoomSummary(with: rootSpaceChildSummaryResponse) + + // Build the child summaries of the queried space + let childInfos = self.spaceChildInfos(from: spaceChildrenResponse, excludedSpaceId: spaceId) + + let spaceChildrenSummary = MXSpaceChildrenSummary(spaceSummary: spaceSummary, childInfos: childInfos) + + self.completionQueue.async { + completion(.success(spaceChildrenSummary)) + } + } + case .failure(let error): + completion(.failure(error)) + } + } + } + + // MARK: - Private + + private func createRoomSummary(with spaceChildSummaryResponse: MXSpaceChildSummaryResponse) -> MXRoomSummary { + + let roomId = spaceChildSummaryResponse.roomId + let roomTypeString = spaceChildSummaryResponse.roomType + + let roomSummary: MXRoomSummary = MXRoomSummary(roomId: roomId, andMatrixSession: nil) + roomSummary.roomTypeString = roomTypeString + roomSummary.roomType = self.roomTypeMapper.roomType(from: roomTypeString) + + let joinedMembersCount = UInt(spaceChildSummaryResponse.numJoinedMembers) + + let membersCount = MXRoomMembersCount() + membersCount.joined = joinedMembersCount + membersCount.members = joinedMembersCount + + roomSummary.membersCount = membersCount + roomSummary.displayname = spaceChildSummaryResponse.name + roomSummary.topic = spaceChildSummaryResponse.topic + roomSummary.avatar = spaceChildSummaryResponse.avatarUrl + roomSummary.isEncrypted = false + + return roomSummary + } + + private func spaceChildInfos(from spaceChildrenResponse: MXSpaceChildrenResponse, excludedSpaceId: String) -> [MXSpaceChildInfo] { + guard let spaceChildSummaries = spaceChildrenResponse.rooms else { + return [] + } + + let childInfos: [MXSpaceChildInfo] = spaceChildSummaries.compactMap { (spaceChildSummaryResponse) -> MXSpaceChildInfo? in + + let spaceId = spaceChildSummaryResponse.roomId + + guard spaceId != excludedSpaceId else { + return nil + } + + let childStateEvent = spaceChildrenResponse.events?.first(where: { (event) -> Bool in + return event.stateKey == spaceId && event.eventType == .spaceChild + }) + + return self.createSpaceChildInfo(with: spaceChildSummaryResponse, and: childStateEvent) + } + + return childInfos + } + + private func createSpaceChildInfo(with spaceChildSummaryResponse: MXSpaceChildSummaryResponse, and spaceChildStateEvent: MXEvent?) -> MXSpaceChildInfo { + + var spaceChildContent: MXSpaceChildContent? + + if let stateEventContent = spaceChildStateEvent?.content { + spaceChildContent = MXSpaceChildContent(fromJSON: stateEventContent) + } + + let roomTypeString = spaceChildSummaryResponse.roomType + let roomType = self.roomTypeMapper.roomType(from: roomTypeString) + + return MXSpaceChildInfo(childRoomId: spaceChildSummaryResponse.roomId, + isKnown: true, + roomTypeString: roomTypeString, + roomType: roomType, + name: spaceChildSummaryResponse.name, + topic: spaceChildSummaryResponse.topic, + avatarUrl: spaceChildSummaryResponse.avatarUrl, + order: spaceChildContent?.order, + activeMemberCount: spaceChildSummaryResponse.numJoinedMembers, + autoJoin: spaceChildContent?.autoJoin ?? false, + viaServers: spaceChildContent?.via ?? [], + parentRoomId: spaceChildStateEvent?.roomId) + } } // MARK: - Objective-C interface @@ -113,11 +267,26 @@ extension MXSpaceService { /// - failure: A closure called when the operation fails. /// - Returns: a `MXHTTPOperation` instance. @discardableResult - public func createSpace(withName name: String, topic: String, isPublic: Bool, success: @escaping (MXSpace) -> Void, failure: @escaping (Error) -> Void) -> MXHTTPOperation { + public func createSpace(withName name: String, topic: String?, isPublic: Bool, success: @escaping (MXSpace) -> Void, failure: @escaping (Error) -> Void) -> MXHTTPOperation { return self.createSpace(withName: name, topic: topic, isPublic: isPublic) { (response) in uncurryResponse(response, success: success, failure: failure) } } + + /// Get the space children informations of a given space from the server. + /// - Parameters: + /// - spaceId: The room id of the queried space. + /// - parameters: Space children request parameters. + /// - completion: A closure called when the operation completes. + /// - Returns: a `MXHTTPOperation` instance. + @discardableResult + public func getSpaceChildrenForSpace(withId spaceId: String, + parameters: MXSpaceChildrenRequestParameters?, + success: @escaping (MXSpaceChildrenSummary) -> Void, failure: @escaping (Error) -> Void) -> MXHTTPOperation { + return self.getSpaceChildrenForSpace(withId: spaceId, parameters: parameters) { (response) in + uncurryResponse(response, success: success, failure: failure) + } + } } // MARK: - Internal room additions diff --git a/MatrixSDK/Utils/MXTools.m b/MatrixSDK/Utils/MXTools.m index 809954c243..42138ca695 100644 --- a/MatrixSDK/Utils/MXTools.m +++ b/MatrixSDK/Utils/MXTools.m @@ -125,7 +125,8 @@ + (void)initialize kMXEventTypeStringSecretRequest, kMXEventTypeStringSecretSend, kMXEventTypeStringSecretStorageDefaultKey, - kMXEventTypeStringTaggedEvents + kMXEventTypeStringTaggedEvents, + kMXEventTypeStringSpaceChild ]; NSMutableDictionary *map = [NSMutableDictionary dictionaryWithCapacity:eventTypeMapEnumToString.count]; diff --git a/MatrixSDKTests/MXSpaceChildContentTests.swift b/MatrixSDKTests/MXSpaceChildContentTests.swift new file mode 100644 index 0000000000..69ba859441 --- /dev/null +++ b/MatrixSDKTests/MXSpaceChildContentTests.swift @@ -0,0 +1,71 @@ +// +// 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 + +class MXSpaceChildContentTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + /// Test child content parsing + func testChildContentParsingSuccess() throws { + + let expectedOrder = "2134" + let expectedSuggested = true + + let json: [String: Any] = [ + "via": ["matrix.org"], + "order": expectedOrder, + "suggested": expectedSuggested + ] + + let spaceChildContent = MXSpaceChildContent(fromJSON: json) + + XCTAssert(spaceChildContent?.order == expectedOrder) + XCTAssert(spaceChildContent?.autoJoin == false) + XCTAssert(spaceChildContent?.suggested == expectedSuggested) + } + + /// Test child content order field valid + func testChildContentParsingOrderValid() throws { + let order = "2134" + + let json: [String: Any] = [ + "order": order + ] + + let spaceChildContent = MXSpaceChildContent(fromJSON: json) + XCTAssert(spaceChildContent?.order == order) + } + + /// Test child content order field not valid + func testChildContentParsingOrderNotValid() throws { + + let json: [String: Any] = [ + "order": "a\nb" + ] + + let spaceChildContent = MXSpaceChildContent(fromJSON: json) + XCTAssertNil(spaceChildContent?.order) + } + +} diff --git a/MatrixSDKTests/MXSpaceServiceTest.swift b/MatrixSDKTests/MXSpaceServiceTest.swift index 2605afcef7..fe4ec24328 100644 --- a/MatrixSDKTests/MXSpaceServiceTest.swift +++ b/MatrixSDKTests/MXSpaceServiceTest.swift @@ -18,6 +18,10 @@ import XCTest import MatrixSDK +enum MXSpaceServiceTestError: Error { + case spaceCreationFailed +} + class MXSpaceServiceTest: XCTestCase { // MARK: - Properties @@ -48,6 +52,39 @@ class MXSpaceServiceTest: XCTestCase { } } + private func createSpaces(with spaceService: MXSpaceService, spaceNames: [String], completion: @escaping (_ spaces: MXResponse<[MXSpace]>) -> Void) { + + let dispatchGroup = DispatchGroup() + + var spaces: [MXSpace] = [] + + for spaceName in spaceNames { + + dispatchGroup.enter() + + spaceService.createSpace(withName: spaceName, topic: nil, isPublic: true) { (response) in + + switch response { + case .success(let space): + spaces.append(space) + case .failure(let error): + XCTFail("Fail to create space named: \(spaceName) with error: \(error)") + } + + dispatchGroup.leave() + } + } + + dispatchGroup.notify(queue: .main) { + + if spaces.count == spaceNames.count { + completion(.success(spaces)) + } else { + completion(.failure(MXSpaceServiceTestError.spaceCreationFailed)) + } + } + } + private func waitRoomSummaryUpdate(for roomId: String, completion: @escaping ((MXRoomSummary) -> Void)) { var token: NSObjectProtocol? @@ -80,10 +117,12 @@ class MXSpaceServiceTest: XCTestCase { /// -> Bob must see the created space with default parameters set func testCreateSpace() throws { + // Create Bob and setup Bob session self.doSpaceServiceTestWithBob(testCase: self) { (spaceService, _, expectation) in let creationParameters = MXSpaceCreationParameters() + // Create space with default parameters spaceService.createSpace(with: creationParameters) { (response) in switch response { case .success(let space): @@ -125,11 +164,14 @@ class MXSpaceServiceTest: XCTestCase { /// /// -> Bob must see the created space with name and topic set func testCreatePublicSpace() throws { + + // Create Bob and setup Bob session self.doSpaceServiceTestWithBob(testCase: self) { (spaceService, _, expectation) in let expectedSpaceName = "Space name" let expectedSpaceTopic = "Space topic" + // Create a public space spaceService.createSpace(withName: expectedSpaceName, topic: expectedSpaceTopic, isPublic: true) { (response) in switch response { case .success(let space): @@ -169,4 +211,190 @@ class MXSpaceServiceTest: XCTestCase { } } } + + /// - Create Bob + /// - Setup Bob session + /// - Create root public space A + /// - Create a child space B with space A as parent + /// + /// -> Bob must see the child space state event of space B + func testAddChildSpace() throws { + + // Create Bob and setup Bob session + self.doSpaceServiceTestWithBob(testCase: self) { (spaceService, session, expectation) in + + let expectedRootSpaceName = "Space A" + let expectedChildSpaceName = "Space B" + + // Create two spaces + self.createSpaces(with: spaceService, spaceNames: [ + expectedRootSpaceName, + expectedChildSpaceName + ]) { (response) in + switch response { + case .success(let spaces): + let rootSpace = spaces[0] + let childSpace = spaces[1] + + // Add space A as child of space B + rootSpace.addChild(roomId: childSpace.spaceId) { (response) in + switch response { + case .success: + + // Make an initial sync and and get the root space A + session.start { (response) in + switch response { + case .success: + + guard let foundRootSpace = spaceService.getSpace(withId: rootSpace.spaceId) else { + XCTFail("Fail to found the root space") + return + } + + // Check if space A contains the space child state event for space B + foundRootSpace.room.state({ (roomState) in + + let stateEvent = roomState?.stateEvents(with: .spaceChild)?.first + + XCTAssert(stateEvent?.stateKey == childSpace.spaceId) + + expectation.fulfill() + }) + case .failure(let error): + XCTFail("Sync failed with error\(error)") + expectation.fulfill() + } + } + + case .failure(let error): + XCTFail("Add child space failed with error \(error)") + expectation.fulfill() + } + } + case .failure(let error): + XCTFail("Create spaces failed with error \(error)") + expectation.fulfill() + } + } + } + } + + /// - Create Bob + /// - Setup Bob session + /// - Create spaces: A, B, C, D + /// - Add B as child of A, C and D as child of B + /// - Call space API with space B identifier + /// + /// -> Bob must see the child space summary of the space B with informations of his children C and D + func testGetSpaceChildren() throws { + + // Create Bob and setup Bob session + self.doSpaceServiceTestWithBob(testCase: self) { (spaceService, session, expectation) in + + let expectedSpaceAName = "Space A" + let expectedSpaceBName = "Space B" + let expectedSpaceCName = "Space C" + let expectedSpaceDName = "Space D" + + // Create 4 spaces + self.createSpaces(with: spaceService, spaceNames: [ + expectedSpaceAName, + expectedSpaceBName, + expectedSpaceCName, + expectedSpaceDName + ]) { (response) in + switch response { + case .success(let spaces): + let spaceA = spaces[0] + let spaceB = spaces[1] + let spaceC = spaces[2] + let spaceD = spaces[3] + + let dispatchGroup = DispatchGroup() + + dispatchGroup.enter() + + // Add B as child of A + spaceA.addChild(roomId: spaceB.spaceId) { (response) in + switch response { + case .success: + break + case .failure(let error): + XCTFail("Add child space failed with error \(error)") + } + + dispatchGroup.leave() + } + + dispatchGroup.enter() + + // Add C as child of B + spaceB.addChild(roomId: spaceC.spaceId) { (response) in + switch response { + case .success: + break + case .failure(let error): + XCTFail("Add child space failed with error \(error)") + } + + dispatchGroup.leave() + } + + dispatchGroup.enter() + + // Add D as child of B + spaceB.addChild(roomId: spaceD.spaceId) { (response) in + switch response { + case .success: + break + case .failure(let error): + XCTFail("Add child space failed with error \(error)") + } + + dispatchGroup.leave() + } + + // wait for space children being added to their parents + dispatchGroup.notify(queue: .main) { + + // Get space children of B node + spaceService.getSpaceChildrenForSpace(withId: spaceB.spaceId, parameters: nil) { (response) in + + XCTAssertTrue(Thread.isMainThread) + + switch response { + case .success(let spaceChildrenSummary): + + XCTAssert(spaceChildrenSummary.spaceSummary.displayname == spaceB.summary?.displayname) + + let childInfos = spaceChildrenSummary.childInfos + + XCTAssert(childInfos.count == 2) + + let childInfoSpaceC = childInfos.first { (childInfo) -> Bool in + childInfo.name == spaceC.summary?.displayname + } + + let childInfoSpaceD = childInfos.first { (childInfo) -> Bool in + childInfo.name == spaceD.summary?.displayname + } + + XCTAssertNotNil(childInfoSpaceC) + XCTAssertNotNil(childInfoSpaceD) + + expectation.fulfill() + case .failure(let error): + XCTFail("Get space children failed with error \(error)") + expectation.fulfill() + } + } + } + + case .failure(let error): + XCTFail("Create spaces failed with error \(error)") + expectation.fulfill() + } + } + } + } }