From bde2d6f2e04955d88304a7a09e150be2a1636c40 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 22 May 2019 21:30:17 +0200 Subject: [PATCH 01/15] Aggregations: Move reactions logic to a dedicated class --- MatrixSDK.xcodeproj/project.pbxproj | 10 +- .../MXAggregatedReactionsUpdater.h | 43 +++ .../MXAggregatedReactionsUpdater.m | 341 ++++++++++++++++++ MatrixSDK/Aggregations/MXAggregations.m | 311 +--------------- 4 files changed, 411 insertions(+), 294 deletions(-) create mode 100644 MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h create mode 100644 MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index db8b0fa25f..1215b3ffe7 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -82,7 +82,7 @@ 322A51D81D9E846800C8536D /* MXCryptoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 322A51D71D9E846800C8536D /* MXCryptoTests.m */; }; 322D01C422492B0700150C68 /* MXCryptoShareTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 322D01C322492B0700150C68 /* MXCryptoShareTests.m */; }; 32322A481E57264E005DD155 /* MXSelfSignedHomeserverTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32322A471E57264E005DD155 /* MXSelfSignedHomeserverTests.m */; }; - 32322A4B1E575F65005DD155 /* MXAllowedCertificates.h in Headers */ = {isa = PBXBuildFile; fileRef = 32322A491E575F65005DD155 /* MXAllowedCertificates.h */; }; + 32322A4B1E575F65005DD155 /* MXAllowedCertificates.h in Headers */ = {isa = PBXBuildFile; fileRef = 32322A491E575F65005DD155 /* MXAllowedCertificates.h */; settings = {ATTRIBUTES = (Public, ); }; }; 32322A4C1E575F65005DD155 /* MXAllowedCertificates.m in Sources */ = {isa = PBXBuildFile; fileRef = 32322A4A1E575F65005DD155 /* MXAllowedCertificates.m */; }; 3233606F1A403A0D0071A488 /* MXFileStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 3233606D1A403A0D0071A488 /* MXFileStore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 323360701A403A0D0071A488 /* MXFileStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 3233606E1A403A0D0071A488 /* MXFileStore.m */; }; @@ -176,6 +176,8 @@ 3275FD9921A6B53300B9C13D /* MXLoginPolicyData.m in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD9721A6B53300B9C13D /* MXLoginPolicyData.m */; }; 3275FD9C21A6B60B00B9C13D /* MXLoginPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 3275FD9A21A6B60B00B9C13D /* MXLoginPolicy.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3275FD9D21A6B60B00B9C13D /* MXLoginPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD9B21A6B60B00B9C13D /* MXLoginPolicy.m */; }; + 32792BD42295A86600F4FC9D /* MXAggregatedReactionsUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */; }; + 32792BD52295A86600F4FC9D /* MXAggregatedReactionsUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BD32295A86600F4FC9D /* MXAggregatedReactionsUpdater.m */; }; 327E37B61A974F75007F026F /* MXLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E37B41A974F75007F026F /* MXLogger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 327E37B71A974F75007F026F /* MXLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E37B51A974F75007F026F /* MXLogger.m */; }; 327E37B91A977810007F026F /* MXLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E37B81A977810007F026F /* MXLoggerTests.m */; }; @@ -624,6 +626,8 @@ 3275FD9721A6B53300B9C13D /* MXLoginPolicyData.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXLoginPolicyData.m; sourceTree = ""; }; 3275FD9A21A6B60B00B9C13D /* MXLoginPolicy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLoginPolicy.h; sourceTree = ""; }; 3275FD9B21A6B60B00B9C13D /* MXLoginPolicy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXLoginPolicy.m; sourceTree = ""; }; + 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXAggregatedReactionsUpdater.h; sourceTree = ""; }; + 32792BD32295A86600F4FC9D /* MXAggregatedReactionsUpdater.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAggregatedReactionsUpdater.m; sourceTree = ""; }; 327E37B41A974F75007F026F /* MXLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXLogger.h; sourceTree = ""; }; 327E37B51A974F75007F026F /* MXLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXLogger.m; sourceTree = ""; }; 327E37B81A977810007F026F /* MXLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXLoggerTests.m; sourceTree = ""; }; @@ -1341,6 +1345,8 @@ 327E9AED2289C61000A98BC1 /* MXAggregations.h */, 327E9AEE2289C61100A98BC1 /* MXAggregations.m */, 327E9AF12289C6B300A98BC1 /* MXAggregations_Private.h */, + 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */, + 32792BD32295A86600F4FC9D /* MXAggregatedReactionsUpdater.m */, ); path = Aggregations; sourceTree = ""; @@ -1973,6 +1979,7 @@ 32A9E8251EF4026E0081358A /* MXUIKitBackgroundModeHandler.h in Headers */, 325D1C261DFECE0D0070B8BF /* MXCrypto_Private.h in Headers */, 32E402B921C957D2004E87A6 /* MXOlmSession.h in Headers */, + 32792BD42295A86600F4FC9D /* MXAggregatedReactionsUpdater.h in Headers */, B146D4D621A5A44E00D8C2C6 /* MXScanRealmInMemoryProvider.h in Headers */, B17285792100C8EA0052C51E /* MXSendReplyEventStringsLocalizable.h in Headers */, 32A151521DAF8A7200400192 /* MXQueuedEncryption.h in Headers */, @@ -2321,6 +2328,7 @@ 32A151471DAF7C0C00400192 /* MXDeviceInfo.m in Sources */, 321CFDEB22525DEE004D31DF /* MXIncomingSASTransaction.m in Sources */, 32A1515C1DB525DA00400192 /* NSObject+sortedKeys.m in Sources */, + 32792BD52295A86600F4FC9D /* MXAggregatedReactionsUpdater.m in Sources */, 32618E7C20EFA45B00E1D2EA /* MXRoomMembers.m in Sources */, 02CAD43A217DD12F0074700B /* MXContentScanEncryptedBody.m in Sources */, F03EF5091DF071D5009DF592 /* MXEncryptedAttachments.m in Sources */, diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h new file mode 100644 index 0000000000..bdc5956d8a --- /dev/null +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h @@ -0,0 +1,43 @@ +/* + Copyright 2019 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 "MXAggregatedReactions.h" + +#import "MXStore.h" +#import "MXAggregationsStore.h" +#import "MXReactionCountChangeListener.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MXAggregatedReactionsUpdater : NSObject + +- (instancetype)initWithMyUser:(NSString*)userId + aggregationStore:(id)store + matrixStore:(id)matrixStore; + +- (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId; + +- (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; +- (void)removeListener:(id)listener; + +- (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction; +- (void)handleRedaction:(MXEvent *)event; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m new file mode 100644 index 0000000000..90db427bb2 --- /dev/null +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m @@ -0,0 +1,341 @@ +/* + Copyright 2019 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 "MXAggregatedReactionsUpdater.h" + +#import "MXEventUnsignedData.h" +#import "MXEventRelations.h" +#import "MXEventAnnotationChunk.h" +#import "MXEventAnnotation.h" + +@interface MXAggregatedReactionsUpdater () + +@property (nonatomic, weak) NSString *myUserId; +@property (nonatomic, weak) id matrixStore; +@property (nonatomic, weak) id store; +@property (nonatomic) NSMutableArray *listeners; + +@end + +@implementation MXAggregatedReactionsUpdater + +- (instancetype)initWithMyUser:(NSString *)userId aggregationStore:(id)store matrixStore:(id)matrixStore +{ + self = [super init]; + if (self) + { + self.myUserId = userId; + self.store = store; + self.matrixStore = matrixStore; + + self.listeners = [NSMutableArray array]; + } + return self; +} + +- (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId +{ + NSArray *reactions = [self.store reactionCountsOnEvent:eventId]; + if (!reactions) + { + reactions = [self reactionCountsFromMatrixStoreOnEvent:eventId inRoom:roomId]; + } + + if (!reactions) + { + // Check reaction data from the hack + reactions = [self reactionCountsUsingHackOnEvent:eventId inRoom:roomId]; + } + + MXAggregatedReactions *aggregatedReactions; + if (reactions) + { + aggregatedReactions = [MXAggregatedReactions new]; + aggregatedReactions.reactions = reactions; + } + + return aggregatedReactions; +} + +- (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block +{ + MXReactionCountChangeListener *listener = [MXReactionCountChangeListener new]; + listener.roomId = roomId; + listener.notificationBlock = block; + + [self.listeners addObject:listener]; + + return listener; +} + +- (void)removeListener:(id)listener +{ + [self.listeners removeObject:listener]; +} + + + +- (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction +{ + NSString *parentEventId = event.relatesTo.eventId; + NSString *reaction = event.relatesTo.key; + + if (parentEventId && reaction) + { + // Manage aggregated reactions only for events in timelines we have + MXEvent *parentEvent = [self.matrixStore eventWithEventId:parentEventId inRoom:event.roomId]; + if (parentEvent) + { + [self storeRelationForReaction:reaction toEvent:parentEventId reactionEvent:event]; + + if (direction == MXTimelineDirectionForwards) + { + [self updateReactionCountForReaction:reaction toEvent:parentEventId reactionEvent:event]; + } + } + else + { + [self storeRelationForHackForReaction:reaction toEvent:parentEventId reactionEvent:event]; + } + } + else + { + NSLog(@"[MXAggregations] handleReaction: ERROR: invalid reaction event: %@", event); + } +} + +- (void)handleRedaction:(MXEvent *)event +{ + NSString *redactedEventId = event.redacts; + MXReactionRelation *relation = [self.store reactionRelationWithReactionEventId:redactedEventId]; + + if (relation) + { + [self removeReaction:relation.reaction onEvent:relation.eventId inRoomId:event.roomId]; + [self.store deleteReactionRelation:relation]; + } +} + +#pragma mark - Private methods - + +- (void)storeRelationForReaction:(NSString*)reaction toEvent:(NSString*)eventId reactionEvent:(MXEvent *)reactionEvent +{ + MXReactionRelation *relation = [MXReactionRelation new]; + relation.reaction = reaction; + relation.eventId = eventId; + relation.reactionEventId = reactionEvent.eventId; + + [self.store addReactionRelation:relation inRoom:reactionEvent.roomId]; +} + +- (void)updateReactionCountForReaction:(NSString*)reaction toEvent:(NSString*)eventId reactionEvent:(MXEvent *)reactionEvent +{ + BOOL isANewReaction = NO; + + // Migrate data from matrix store to aggregation store if needed + [self checkAggregationStoreForEvent:eventId inRoomId:reactionEvent.roomId]; + + // Create or update the current reaction count if it exists + MXReactionCount *reactionCount = [self.store reactionCountForReaction:reaction onEvent:eventId]; + if (!reactionCount) + { + // If we still have no reaction count object, create one + reactionCount = [MXReactionCount new]; + reactionCount.reaction = reaction; + isANewReaction = YES; + } + + // Add the reaction + reactionCount.count++; + + // Store reaction made by our user + if ([reactionEvent.sender isEqualToString:self.myUserId]) + { + reactionCount.myUserReactionEventId = reactionEvent.eventId; + } + + // Update store + [self.store addOrUpdateReactionCount:reactionCount onEvent:eventId inRoom:reactionEvent.roomId]; + + // Notify + [self notifyReactionCountChangeListenersOfRoom:reactionEvent.roomId + event:eventId + reactionCount:reactionCount + isNewReaction:isANewReaction]; +} + +- (void)removeReaction:(NSString*)reaction onEvent:(NSString*)eventId inRoomId:(NSString*)roomId +{ + // Migrate data from matrix store to aggregation store if needed + [self checkAggregationStoreForEvent:eventId inRoomId:roomId]; + + // Create or update the current reaction count if it exists + MXReactionCount *reactionCount = [self.store reactionCountForReaction:reaction onEvent:eventId]; + if (reactionCount) + { + if (reactionCount.count > 1) + { + reactionCount.count--; + + [self.store addOrUpdateReactionCount:reactionCount onEvent:eventId inRoom:roomId]; + [self notifyReactionCountChangeListenersOfRoom:roomId + event:eventId + reactionCount:reactionCount + isNewReaction:NO]; + } + else + { + [self.store deleteReactionCountsForReaction:reaction onEvent:eventId]; + [self notifyReactionCountChangeListenersOfRoom:roomId event:eventId forDeletedReaction:reaction]; + } + } +} + +// If not already done, copy aggregation data from matrix store to aggregation store +- (void)checkAggregationStoreForEvent:(NSString*)eventId inRoomId:(NSString*)roomId +{ + if (![self.store hasReactionCountsOnEvent:eventId]) + { + NSArray *reactions = [self reactionCountsFromMatrixStoreOnEvent:eventId inRoom:roomId]; + + if (!reactions) + { + // Check reaction data from the hack + reactions = [self reactionCountsUsingHackOnEvent:eventId inRoom:roomId]; + } + + if (reactions) + { + [self.store setReactionCounts:reactions onEvent:eventId inRoom:roomId]; + } + } +} + +- (nullable NSArray *)reactionCountsFromMatrixStoreOnEvent:(NSString*)eventId inRoom:(NSString*)roomId +{ + NSMutableArray *reactions; + + MXEvent *event = [self.matrixStore eventWithEventId:eventId inRoom:roomId]; + if (event) + { + for (MXEventAnnotation *annotation in event.unsignedData.relations.annotation.chunk) + { + if ([annotation.type isEqualToString:MXEventAnnotationReaction]) + { + MXReactionCount *reactionCount = [MXReactionCount new]; + reactionCount.reaction = annotation.key; + reactionCount.count = annotation.count; + + if (!reactions) + { + reactions = [NSMutableArray array]; + } + [reactions addObject:reactionCount]; + } + } + } + + return reactions; +} + +- (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId event:(NSString*)eventId reactionCount:(MXReactionCount*)reactionCount isNewReaction:(BOOL)isNewReaction +{ + MXReactionCountChange *reactionCountChange = [MXReactionCountChange new]; + if (isNewReaction) + { + reactionCountChange.inserted = @[reactionCount]; + } + else + { + reactionCountChange.modified = @[reactionCount]; + } + + [self notifyReactionCountChangeListenersOfRoom:roomId changes:@{ + eventId:reactionCountChange + }]; +} + +- (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId event:(NSString*)eventId forDeletedReaction:(NSString*)deletedReaction +{ + MXReactionCountChange *reactionCountChange = [MXReactionCountChange new]; + reactionCountChange.deleted = @[deletedReaction]; + + [self notifyReactionCountChangeListenersOfRoom:roomId changes:@{ + eventId:reactionCountChange + }]; +} + +- (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId changes:(NSDictionary*)changes +{ + for (MXReactionCountChangeListener *listener in self.listeners) + { + if ([listener.roomId isEqualToString:roomId]) + { + listener.notificationBlock(changes); + } + } +} + +#pragma mark - Reactions hack (TODO: Remove all methods) - +/// TODO: To remove once the feature has landed on matrix.org homeserver + + +// Compute reactions counts from relations we know +// Note: This is not accurate and will be removed soon +- (nullable NSArray *)reactionCountsUsingHackOnEvent:(NSString*)eventId inRoom:(NSString*)roomId +{ + NSDate *startDate = [NSDate date]; + + NSMutableDictionary *reactionCountDict = [NSMutableDictionary dictionary]; + + NSArray *relations = [self.store reactionRelationsOnEvent:eventId]; + for (MXReactionRelation *relation in relations) + { + MXReactionCount *reactionCount = reactionCountDict[relation.reaction]; + if (!reactionCount) + { + reactionCount = [MXReactionCount new]; + reactionCount.reaction = relation.reaction; + reactionCountDict[relation.reaction] = reactionCount; + } + + reactionCount.count++; + + if (!reactionCount.myUserReactionEventId) + { + // Determine if my user has reacted + MXEvent *event = [self.matrixStore eventWithEventId:relation.reactionEventId inRoom:roomId]; + if ([event.sender isEqualToString:self.myUserId]) + { + reactionCount.myUserReactionEventId = relation.reactionEventId; + } + } + } + + NSLog(@"[MXAggregations] reactionCountsUsingHackOnEvent: Build %@ reactionCounts in %.0fms", + @(reactionCountDict.count), + [[NSDate date] timeIntervalSinceDate:startDate] * 1000); + + return reactionCountDict.allValues; +} + +// We need to store all received relations even if we do not know the event yet +- (void)storeRelationForHackForReaction:(NSString*)reaction toEvent:(NSString*)eventId reactionEvent:(MXEvent *)reactionEvent +{ + [self storeRelationForReaction:reaction toEvent:eventId reactionEvent:reactionEvent]; +} + +@end diff --git a/MatrixSDK/Aggregations/MXAggregations.m b/MatrixSDK/Aggregations/MXAggregations.m index b6d8e89363..e136776b62 100644 --- a/MatrixSDK/Aggregations/MXAggregations.m +++ b/MatrixSDK/Aggregations/MXAggregations.m @@ -21,21 +21,17 @@ #import "MXSession.h" #import "MXTools.h" -#import "MXEventUnsignedData.h" #import "MXEventRelations.h" -#import "MXEventAnnotationChunk.h" -#import "MXEventAnnotation.h" #import "MXRealmAggregationsStore.h" -#import "MXReactionCountChangeListener.h" +#import "MXAggregatedReactionsUpdater.h" @interface MXAggregations () @property (nonatomic, weak) MXSession *mxSession; -@property (nonatomic, weak) id matrixStore; @property (nonatomic) id store; -@property (nonatomic) NSMutableArray *listeners; +@property (nonatomic) MXAggregatedReactionsUpdater *aggregatedReactionsUpdater; @end @@ -107,42 +103,17 @@ - (MXHTTPOperation*)unReactOnReaction:(NSString*)reaction - (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId { - NSArray *reactions = [self.store reactionCountsOnEvent:eventId]; - if (!reactions) - { - reactions = [self reactionCountsFromMatrixStoreOnEvent:eventId inRoom:roomId]; - } - - if (!reactions) - { - // Check reaction data from the hack - reactions = [self reactionCountsUsingHackOnEvent:eventId inRoom:roomId]; - } - - MXAggregatedReactions *aggregatedReactions; - if (reactions) - { - aggregatedReactions = [MXAggregatedReactions new]; - aggregatedReactions.reactions = reactions; - } - - return aggregatedReactions; + return [self.aggregatedReactionsUpdater aggregatedReactionsOnEvent:eventId inRoom:roomId]; } - (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block { - MXReactionCountChangeListener *listener = [MXReactionCountChangeListener new]; - listener.roomId = roomId; - listener.notificationBlock = block; - - [self.listeners addObject:listener]; - - return listener; + return [self.aggregatedReactionsUpdater listenToReactionCountUpdateInRoom:roomId block:block]; } - (void)removeListener:(id)listener { - [self.listeners removeObject:listener]; + [self.aggregatedReactionsUpdater removeListener:listener]; } - (void)resetData @@ -159,20 +130,21 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession if (self) { self.mxSession = mxSession; - self.matrixStore = mxSession.store; self.store = [[MXRealmAggregationsStore alloc] initWithCredentials:mxSession.matrixRestClient.credentials]; - self.listeners = [NSMutableArray array]; + + self.aggregatedReactionsUpdater = [[MXAggregatedReactionsUpdater alloc] initWithMyUser:self.mxSession.myUser.userId + aggregationStore:self.store matrixStore:mxSession.store]; [mxSession listenToEventsOfTypes:@[kMXEventTypeStringReaction, kMXEventTypeStringRoomRedaction] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { switch (event.eventType) { case MXEventTypeReaction: - [self handleReaction:event direction:direction]; + [self.aggregatedReactionsUpdater handleReaction:event direction:direction]; break; case MXEventTypeRoomRedaction: if (direction == MXTimelineDirectionForwards) { - [self handleRedaction:event]; + [self.aggregatedReactionsUpdater handleRedaction:event]; } break; default: @@ -190,217 +162,15 @@ - (void)resetDataInRoom:(NSString *)roomId [self.store deleteAllReactionRelationsInRoom:roomId]; } - -#pragma mark - Private methods - - -- (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction -{ - NSString *parentEventId = event.relatesTo.eventId; - NSString *reaction = event.relatesTo.key; - - if (parentEventId && reaction) - { - // Manage aggregated reactions only for events in timelines we have - MXEvent *parentEvent = [self.matrixStore eventWithEventId:parentEventId inRoom:event.roomId]; - if (parentEvent) - { - [self storeRelationForReaction:reaction toEvent:parentEventId reactionEvent:event]; - - if (direction == MXTimelineDirectionForwards) - { - [self updateReactionCountForReaction:reaction toEvent:parentEventId reactionEvent:event]; - } - } - else - { - [self storeRelationForHackForReaction:reaction toEvent:parentEventId reactionEvent:event]; - } - } - else - { - NSLog(@"[MXAggregations] handleReaction: ERROR: invalid reaction event: %@", event); - } -} - -- (void)handleRedaction:(MXEvent *)event -{ - NSString *redactedEventId = event.redacts; - MXReactionRelation *relation = [self.store reactionRelationWithReactionEventId:redactedEventId]; - - if (relation) - { - [self removeReaction:relation.reaction onEvent:relation.eventId inRoomId:event.roomId]; - [self.store deleteReactionRelation:relation]; - } -} - -- (void)storeRelationForReaction:(NSString*)reaction toEvent:(NSString*)eventId reactionEvent:(MXEvent *)reactionEvent -{ - MXReactionRelation *relation = [MXReactionRelation new]; - relation.reaction = reaction; - relation.eventId = eventId; - relation.reactionEventId = reactionEvent.eventId; - - [self.store addReactionRelation:relation inRoom:reactionEvent.roomId]; -} - -- (void)updateReactionCountForReaction:(NSString*)reaction toEvent:(NSString*)eventId reactionEvent:(MXEvent *)reactionEvent -{ - BOOL isANewReaction = NO; - - // Migrate data from matrix store to aggregation store if needed - [self checkAggregationStoreForEvent:eventId inRoomId:reactionEvent.roomId]; - - // Create or update the current reaction count if it exists - MXReactionCount *reactionCount = [self.store reactionCountForReaction:reaction onEvent:eventId]; - if (!reactionCount) - { - // If we still have no reaction count object, create one - reactionCount = [MXReactionCount new]; - reactionCount.reaction = reaction; - isANewReaction = YES; - } - - // Add the reaction - reactionCount.count++; - - // Store reaction made by our user - if ([reactionEvent.sender isEqualToString:self.mxSession.myUser.userId]) - { - reactionCount.myUserReactionEventId = reactionEvent.eventId; - } - - // Update store - [self.store addOrUpdateReactionCount:reactionCount onEvent:eventId inRoom:reactionEvent.roomId]; - - // Notify - [self notifyReactionCountChangeListenersOfRoom:reactionEvent.roomId - event:eventId - reactionCount:reactionCount - isNewReaction:isANewReaction]; -} - -- (void)removeReaction:(NSString*)reaction onEvent:(NSString*)eventId inRoomId:(NSString*)roomId -{ - // Migrate data from matrix store to aggregation store if needed - [self checkAggregationStoreForEvent:eventId inRoomId:roomId]; - - // Create or update the current reaction count if it exists - MXReactionCount *reactionCount = [self.store reactionCountForReaction:reaction onEvent:eventId]; - if (reactionCount) - { - if (reactionCount.count > 1) - { - reactionCount.count--; - - [self.store addOrUpdateReactionCount:reactionCount onEvent:eventId inRoom:roomId]; - [self notifyReactionCountChangeListenersOfRoom:roomId - event:eventId - reactionCount:reactionCount - isNewReaction:NO]; - } - else - { - [self.store deleteReactionCountsForReaction:reaction onEvent:eventId]; - [self notifyReactionCountChangeListenersOfRoom:roomId event:eventId forDeletedReaction:reaction]; - } - } -} - -// If not already done, copy aggregation data from matrix store to aggregation store -- (void)checkAggregationStoreForEvent:(NSString*)eventId inRoomId:(NSString*)roomId -{ - if (![self.store hasReactionCountsOnEvent:eventId]) - { - NSArray *reactions = [self reactionCountsFromMatrixStoreOnEvent:eventId inRoom:roomId]; - - if (!reactions) - { - // Check reaction data from the hack - reactions = [self reactionCountsUsingHackOnEvent:eventId inRoom:roomId]; - } - - if (reactions) - { - [self.store setReactionCounts:reactions onEvent:eventId inRoom:roomId]; - } - } -} - -- (nullable NSArray *)reactionCountsFromMatrixStoreOnEvent:(NSString*)eventId inRoom:(NSString*)roomId -{ - NSMutableArray *reactions; - - MXEvent *event = [self.matrixStore eventWithEventId:eventId inRoom:roomId]; - if (event) - { - for (MXEventAnnotation *annotation in event.unsignedData.relations.annotation.chunk) - { - if ([annotation.type isEqualToString:MXEventAnnotationReaction]) - { - MXReactionCount *reactionCount = [MXReactionCount new]; - reactionCount.reaction = annotation.key; - reactionCount.count = annotation.count; - - if (!reactions) - { - reactions = [NSMutableArray array]; - } - [reactions addObject:reactionCount]; - } - } - } - - return reactions; -} - -- (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId event:(NSString*)eventId reactionCount:(MXReactionCount*)reactionCount isNewReaction:(BOOL)isNewReaction -{ - MXReactionCountChange *reactionCountChange = [MXReactionCountChange new]; - if (isNewReaction) - { - reactionCountChange.inserted = @[reactionCount]; - } - else - { - reactionCountChange.modified = @[reactionCount]; - } - - [self notifyReactionCountChangeListenersOfRoom:roomId changes:@{ - eventId:reactionCountChange - }]; -} - -- (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId event:(NSString*)eventId forDeletedReaction:(NSString*)deletedReaction -{ - MXReactionCountChange *reactionCountChange = [MXReactionCountChange new]; - reactionCountChange.deleted = @[deletedReaction]; - - [self notifyReactionCountChangeListenersOfRoom:roomId changes:@{ - eventId:reactionCountChange - }]; -} - -- (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId changes:(NSDictionary*)changes -{ - for (MXReactionCountChangeListener *listener in self.listeners) - { - if ([listener.roomId isEqualToString:roomId]) - { - listener.notificationBlock(changes); - } - } -} - #pragma mark - Reactions hack (TODO: Remove all methods) - /// TODO: To remove once the feature has landed on matrix.org homeserver // SendReactionUsingHack directly sends a `m.reaction` room message instead of using the `/send_relation` api. - (MXHTTPOperation*)sendReactionUsingHack:(NSString*)reaction - toEvent:(NSString*)eventId - inRoom:(NSString*)roomId - success:(void (^)(NSString *eventId))success - failure:(void (^)(NSError *error))failure + toEvent:(NSString*)eventId + inRoom:(NSString*)roomId + success:(void (^)(NSString *eventId))success + failure:(void (^)(NSError *error))failure { NSLog(@"[MXAggregations] sendReactionUsingHack"); @@ -413,58 +183,13 @@ - (MXHTTPOperation*)sendReactionUsingHack:(NSString*)reaction NSDictionary *reactionContent = @{ @"m.relates_to": @{ - @"rel_type": @"m.annotation", - @"event_id": eventId, - @"key": reaction - } + @"rel_type": @"m.annotation", + @"event_id": eventId, + @"key": reaction + } }; return [room sendEventOfType:kMXEventTypeStringReaction content:reactionContent localEcho:nil success:success failure:failure]; } -// Compute reactions counts from relations we know -// Note: This is not accurate and will be removed soon -- (nullable NSArray *)reactionCountsUsingHackOnEvent:(NSString*)eventId inRoom:(NSString*)roomId -{ - NSDate *startDate = [NSDate date]; - - NSMutableDictionary *reactionCountDict = [NSMutableDictionary dictionary]; - - NSArray *relations = [self.store reactionRelationsOnEvent:eventId]; - for (MXReactionRelation *relation in relations) - { - MXReactionCount *reactionCount = reactionCountDict[relation.reaction]; - if (!reactionCount) - { - reactionCount = [MXReactionCount new]; - reactionCount.reaction = relation.reaction; - reactionCountDict[relation.reaction] = reactionCount; - } - - reactionCount.count++; - - if (!reactionCount.myUserReactionEventId) - { - // Determine if my user has reacted - MXEvent *event = [self.matrixStore eventWithEventId:relation.reactionEventId inRoom:roomId]; - if ([event.sender isEqualToString:self.mxSession.myUser.userId]) - { - reactionCount.myUserReactionEventId = relation.reactionEventId; - } - } - } - - NSLog(@"[MXAggregations] reactionCountsUsingHackOnEvent: Build %@ reactionCounts in %.0fms", - @(reactionCountDict.count), - [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - - return reactionCountDict.allValues; -} - -// We need to store all received relations even if we do not know the event yet -- (void)storeRelationForHackForReaction:(NSString*)reaction toEvent:(NSString*)eventId reactionEvent:(MXEvent *)reactionEvent -{ - [self storeRelationForReaction:reaction toEvent:eventId reactionEvent:reactionEvent]; -} - @end From e00b55dc70d88d58e8954803c4bf31e9dffe5ab4 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 23 May 2019 07:56:20 +0200 Subject: [PATCH 02/15] Reactions: Fix hack to make tests pass --- .../Aggregations/MXAggregatedReactionsUpdater.m | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m index 90db427bb2..97598c63e1 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m @@ -99,12 +99,12 @@ - (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction MXEvent *parentEvent = [self.matrixStore eventWithEventId:parentEventId inRoom:event.roomId]; if (parentEvent) { - [self storeRelationForReaction:reaction toEvent:parentEventId reactionEvent:event]; - if (direction == MXTimelineDirectionForwards) { [self updateReactionCountForReaction:reaction toEvent:parentEventId reactionEvent:event]; } + + [self storeRelationForReaction:reaction toEvent:parentEventId reactionEvent:event]; } else { @@ -124,8 +124,8 @@ - (void)handleRedaction:(MXEvent *)event if (relation) { - [self removeReaction:relation.reaction onEvent:relation.eventId inRoomId:event.roomId]; [self.store deleteReactionRelation:relation]; + [self removeReaction:relation.reaction onEvent:relation.eventId inRoomId:event.roomId]; } } @@ -299,11 +299,17 @@ - (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId changes:(NSDi { NSDate *startDate = [NSDate date]; - NSMutableDictionary *reactionCountDict = [NSMutableDictionary dictionary]; + NSMutableDictionary *reactionCountDict; NSArray *relations = [self.store reactionRelationsOnEvent:eventId]; for (MXReactionRelation *relation in relations) { + if (!reactionCountDict) + { + // Have the same behavior as reactionCountsFromMatrixStoreOnEvent + reactionCountDict = [NSMutableDictionary dictionary]; + } + MXReactionCount *reactionCount = reactionCountDict[relation.reaction]; if (!reactionCount) { From 7a2b67f93b5cb417cdc9874a614493688e9b5074 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 23 May 2019 07:58:08 +0200 Subject: [PATCH 03/15] Aggregations: Need a valid user id --- MatrixSDK/Aggregations/MXAggregations.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/Aggregations/MXAggregations.m b/MatrixSDK/Aggregations/MXAggregations.m index e136776b62..626647a447 100644 --- a/MatrixSDK/Aggregations/MXAggregations.m +++ b/MatrixSDK/Aggregations/MXAggregations.m @@ -132,7 +132,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession self.mxSession = mxSession; self.store = [[MXRealmAggregationsStore alloc] initWithCredentials:mxSession.matrixRestClient.credentials]; - self.aggregatedReactionsUpdater = [[MXAggregatedReactionsUpdater alloc] initWithMyUser:self.mxSession.myUser.userId + self.aggregatedReactionsUpdater = [[MXAggregatedReactionsUpdater alloc] initWithMyUser:mxSession.matrixRestClient.credentials.userId aggregationStore:self.store matrixStore:mxSession.store]; [mxSession listenToEventsOfTypes:@[kMXEventTypeStringReaction, kMXEventTypeStringRoomRedaction] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { From d75003609fb97a3b04158d410e37d8b11248e9e0 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 23 May 2019 08:06:17 +0200 Subject: [PATCH 04/15] Aggregations: Move all reactions store from MXAggregations to MXAggregatedReactionsUpdater --- MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h | 3 +++ MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m | 10 ++++++++++ MatrixSDK/Aggregations/MXAggregations.m | 5 ++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h index bdc5956d8a..eefd1bcf8e 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h @@ -31,6 +31,7 @@ NS_ASSUME_NONNULL_BEGIN matrixStore:(id)matrixStore; - (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId; +- (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId; - (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; - (void)removeListener:(id)listener; @@ -38,6 +39,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction; - (void)handleRedaction:(MXEvent *)event; +- (void)resetDataInRoom:(NSString *)roomId; + @end NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m index 97598c63e1..769c518b34 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m @@ -69,6 +69,10 @@ - (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventI return aggregatedReactions; } +- (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId +{ + return [self.store reactionCountForReaction:reaction onEvent:eventId]; +} - (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block { @@ -129,6 +133,12 @@ - (void)handleRedaction:(MXEvent *)event } } +- (void)resetDataInRoom:(NSString *)roomId +{ + [self.store deleteAllReactionCountsInRoom:roomId]; + [self.store deleteAllReactionRelationsInRoom:roomId]; +} + #pragma mark - Private methods - - (void)storeRelationForReaction:(NSString*)reaction toEvent:(NSString*)eventId reactionEvent:(MXEvent *)reactionEvent diff --git a/MatrixSDK/Aggregations/MXAggregations.m b/MatrixSDK/Aggregations/MXAggregations.m index 626647a447..2d08ab7488 100644 --- a/MatrixSDK/Aggregations/MXAggregations.m +++ b/MatrixSDK/Aggregations/MXAggregations.m @@ -78,7 +78,7 @@ - (MXHTTPOperation*)unReactOnReaction:(NSString*)reaction { MXHTTPOperation *operation; - MXReactionCount *reactionCount = [self.store reactionCountForReaction:reaction onEvent:eventId]; + MXReactionCount *reactionCount = [self.aggregatedReactionsUpdater reactionCountForReaction:reaction onEvent:eventId]; if (reactionCount && reactionCount.myUserReactionEventId) { MXRoom *room = [self.mxSession roomWithRoomId:roomId]; @@ -158,8 +158,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession - (void)resetDataInRoom:(NSString *)roomId { - [self.store deleteAllReactionCountsInRoom:roomId]; - [self.store deleteAllReactionRelationsInRoom:roomId]; + [self.aggregatedReactionsUpdater resetDataInRoom:roomId]; } #pragma mark - Reactions hack (TODO: Remove all methods) - From bc5ef23166600c6cf0f741d45d123b38ad1237cc Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 23 May 2019 14:14:06 +0200 Subject: [PATCH 05/15] MXReactionTests -> MXAggregatedReactionTests --- .../{MXReactionTests.m => MXAggregatedReactionTests.m} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename MatrixSDKTests/{MXReactionTests.m => MXAggregatedReactionTests.m} (99%) diff --git a/MatrixSDKTests/MXReactionTests.m b/MatrixSDKTests/MXAggregatedReactionTests.m similarity index 99% rename from MatrixSDKTests/MXReactionTests.m rename to MatrixSDKTests/MXAggregatedReactionTests.m index e0be4e15f9..50e176ee23 100644 --- a/MatrixSDKTests/MXReactionTests.m +++ b/MatrixSDKTests/MXAggregatedReactionTests.m @@ -29,14 +29,14 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" -@interface MXReactionTests : XCTestCase +@interface MXAggregatedReactionTests : XCTestCase { MatrixSDKTestsData *matrixSDKTestsData; } @end -@implementation MXReactionTests +@implementation MXAggregatedReactionTests - (void)setUp { From b0584e674c2c8d1a280f5748cb3659f3e2121911 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 23 May 2019 16:19:21 +0200 Subject: [PATCH 06/15] Aggregations: Started edits --- MatrixSDK.xcodeproj/project.pbxproj | 20 +- .../Aggregations/MXAggregatedEditsUpdater.h | 43 ++++ .../Aggregations/MXAggregatedEditsUpdater.m | 67 +++++++ MatrixSDK/Aggregations/MXAggregations.h | 23 +++ MatrixSDK/Aggregations/MXAggregations.m | 91 +++++++-- MatrixSDKTests/MXAggregatedEditsTests.m | 187 ++++++++++++++++++ MatrixSDKTests/MXAggregatedReactionTests.m | 32 +++ 7 files changed, 440 insertions(+), 23 deletions(-) create mode 100644 MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h create mode 100644 MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m create mode 100644 MatrixSDKTests/MXAggregatedEditsTests.m diff --git a/MatrixSDK.xcodeproj/project.pbxproj b/MatrixSDK.xcodeproj/project.pbxproj index 1215b3ffe7..5795c0fe8e 100644 --- a/MatrixSDK.xcodeproj/project.pbxproj +++ b/MatrixSDK.xcodeproj/project.pbxproj @@ -178,6 +178,10 @@ 3275FD9D21A6B60B00B9C13D /* MXLoginPolicy.m in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD9B21A6B60B00B9C13D /* MXLoginPolicy.m */; }; 32792BD42295A86600F4FC9D /* MXAggregatedReactionsUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */; }; 32792BD52295A86600F4FC9D /* MXAggregatedReactionsUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BD32295A86600F4FC9D /* MXAggregatedReactionsUpdater.m */; }; + 32792BDC2296B90A00F4FC9D /* MXAggregatedEditsUpdater.h in Headers */ = {isa = PBXBuildFile; fileRef = 32792BDA2296B90A00F4FC9D /* MXAggregatedEditsUpdater.h */; }; + 32792BDD2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BDB2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m */; }; + 32792BDF2296C59B00F4FC9D /* MXAggregatedReactionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BDE2296C59B00F4FC9D /* MXAggregatedReactionTests.m */; }; + 32792BE12296C64200F4FC9D /* MXAggregatedEditsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 32792BE02296C64200F4FC9D /* MXAggregatedEditsTests.m */; }; 327E37B61A974F75007F026F /* MXLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E37B41A974F75007F026F /* MXLogger.h */; settings = {ATTRIBUTES = (Public, ); }; }; 327E37B71A974F75007F026F /* MXLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E37B51A974F75007F026F /* MXLogger.m */; }; 327E37B91A977810007F026F /* MXLoggerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E37B81A977810007F026F /* MXLoggerTests.m */; }; @@ -194,7 +198,6 @@ 327E9AE22285497100A98BC1 /* MXEventContentRelatesTo.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E9AE02285497100A98BC1 /* MXEventContentRelatesTo.m */; }; 327E9AE72285A8C400A98BC1 /* MXAggregationPaginatedResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E9AE52285A8C400A98BC1 /* MXAggregationPaginatedResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 327E9AE82285A8C400A98BC1 /* MXAggregationPaginatedResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E9AE62285A8C400A98BC1 /* MXAggregationPaginatedResponse.m */; }; - 327E9AEA2285DFB600A98BC1 /* MXReactionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E9AE92285DFB600A98BC1 /* MXReactionTests.m */; }; 327E9AEF2289C61100A98BC1 /* MXAggregations.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E9AED2289C61000A98BC1 /* MXAggregations.h */; settings = {ATTRIBUTES = (Public, ); }; }; 327E9AF02289C61100A98BC1 /* MXAggregations.m in Sources */ = {isa = PBXBuildFile; fileRef = 327E9AEE2289C61100A98BC1 /* MXAggregations.m */; }; 327E9AF62289D53800A98BC1 /* MXReactionCount.h in Headers */ = {isa = PBXBuildFile; fileRef = 327E9AF42289D53800A98BC1 /* MXReactionCount.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -628,6 +631,10 @@ 3275FD9B21A6B60B00B9C13D /* MXLoginPolicy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXLoginPolicy.m; sourceTree = ""; }; 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXAggregatedReactionsUpdater.h; sourceTree = ""; }; 32792BD32295A86600F4FC9D /* MXAggregatedReactionsUpdater.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAggregatedReactionsUpdater.m; sourceTree = ""; }; + 32792BDA2296B90A00F4FC9D /* MXAggregatedEditsUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXAggregatedEditsUpdater.h; sourceTree = ""; }; + 32792BDB2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAggregatedEditsUpdater.m; sourceTree = ""; }; + 32792BDE2296C59B00F4FC9D /* MXAggregatedReactionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXAggregatedReactionTests.m; sourceTree = ""; }; + 32792BE02296C64200F4FC9D /* MXAggregatedEditsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAggregatedEditsTests.m; sourceTree = ""; }; 327E37B41A974F75007F026F /* MXLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXLogger.h; sourceTree = ""; }; 327E37B51A974F75007F026F /* MXLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXLogger.m; sourceTree = ""; }; 327E37B81A977810007F026F /* MXLoggerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MXLoggerTests.m; sourceTree = ""; }; @@ -644,7 +651,6 @@ 327E9AE02285497100A98BC1 /* MXEventContentRelatesTo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXEventContentRelatesTo.m; sourceTree = ""; }; 327E9AE52285A8C400A98BC1 /* MXAggregationPaginatedResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXAggregationPaginatedResponse.h; sourceTree = ""; }; 327E9AE62285A8C400A98BC1 /* MXAggregationPaginatedResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAggregationPaginatedResponse.m; sourceTree = ""; }; - 327E9AE92285DFB600A98BC1 /* MXReactionTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXReactionTests.m; sourceTree = ""; }; 327E9AED2289C61000A98BC1 /* MXAggregations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXAggregations.h; sourceTree = ""; }; 327E9AEE2289C61100A98BC1 /* MXAggregations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXAggregations.m; sourceTree = ""; }; 327E9AF12289C6B300A98BC1 /* MXAggregations_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXAggregations_Private.h; sourceTree = ""; }; @@ -1345,6 +1351,8 @@ 327E9AED2289C61000A98BC1 /* MXAggregations.h */, 327E9AEE2289C61100A98BC1 /* MXAggregations.m */, 327E9AF12289C6B300A98BC1 /* MXAggregations_Private.h */, + 32792BDA2296B90A00F4FC9D /* MXAggregatedEditsUpdater.h */, + 32792BDB2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m */, 32792BD22295A86600F4FC9D /* MXAggregatedReactionsUpdater.h */, 32792BD32295A86600F4FC9D /* MXAggregatedReactionsUpdater.m */, ); @@ -1597,7 +1605,8 @@ 32C6F93919DD814400EA4E9C /* MatrixSDKTests */ = { isa = PBXGroup; children = ( - 327E9AE92285DFB600A98BC1 /* MXReactionTests.m */, + 32792BE02296C64200F4FC9D /* MXAggregatedEditsTests.m */, + 32792BDE2296C59B00F4FC9D /* MXAggregatedReactionTests.m */, 327E9ACE2284783E00A98BC1 /* MXEventAnnotationTests.swift */, 329E808E22512DF500A48C3A /* MXCryptoDeviceVerificationTests.m */, 322D01C322492B0700150C68 /* MXCryptoShareTests.m */, @@ -1900,6 +1909,7 @@ 329D3E621E251027002E2F1E /* MXRoomSummaryUpdater.h in Headers */, 321B413F1E09937E009EEEC7 /* MXRoomSummary.h in Headers */, B17982F52119E4A2001FD722 /* MXRoomCreateContent.h in Headers */, + 32792BDC2296B90A00F4FC9D /* MXAggregatedEditsUpdater.h in Headers */, 327E9AEF2289C61100A98BC1 /* MXAggregations.h in Headers */, 32DC15CF1A8CF7AE006F9AD3 /* MXPushRuleConditionChecker.h in Headers */, 32954019216385F100E300FC /* MXServerNoticeContent.h in Headers */, @@ -2242,6 +2252,7 @@ 32B94E06228EE90300716A26 /* MXRealmReactionRelation.m in Sources */, 320BBF411D6C81550079890E /* MXEventsByTypesEnumeratorOnArray.m in Sources */, 3213301E228B190F0070BA9B /* MXRealmAggregationsMapper.m in Sources */, + 32792BDD2296B90A00F4FC9D /* MXAggregatedEditsUpdater.m in Sources */, 3259CD541DF860C300186944 /* MXRealmCryptoStore.m in Sources */, 321B41401E09937E009EEEC7 /* MXRoomSummary.m in Sources */, 32CAB1081A91EA34008C5BB9 /* MXPushRuleRoomMemberCountConditionChecker.m in Sources */, @@ -2442,8 +2453,8 @@ 327137241A24BDDE00DB6757 /* MXUserTests.m in Sources */, 32720DA2222EB5650086FFF5 /* MXAutoDiscoveryTests.m in Sources */, 327E37B91A977810007F026F /* MXLoggerTests.m in Sources */, + 32792BE12296C64200F4FC9D /* MXAggregatedEditsTests.m in Sources */, 329571931B0240CE00ABB3BA /* MXVoIPTests.m in Sources */, - 327E9AEA2285DFB600A98BC1 /* MXReactionTests.m in Sources */, 32A27D1F19EC335300BAFADE /* MXRoomTests.m in Sources */, 32D8CAC219DEE6ED002AF8A0 /* MXRestClientNoAuthAPITests.m in Sources */, 32FCAB4D19E578860049C555 /* MXRestClientTests.m in Sources */, @@ -2451,6 +2462,7 @@ 329FB17C1A0A963700A5E88E /* MXRoomMemberTests.m in Sources */, 3246BDC51A1A0789000A7D62 /* MXRoomStateDynamicTests.m in Sources */, 3264DB941CECA72900B99881 /* MXAccountDataTests.m in Sources */, + 32792BDF2296C59B00F4FC9D /* MXAggregatedReactionTests.m in Sources */, 322D01C422492B0700150C68 /* MXCryptoShareTests.m in Sources */, 323EF7471C7CB4C7000DC98C /* MXEventTimelineTests.m in Sources */, 32E226A91D081CE200E6CA54 /* MXPeekingRoomTests.m in Sources */, diff --git a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h new file mode 100644 index 0000000000..03b5ae3bc7 --- /dev/null +++ b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h @@ -0,0 +1,43 @@ +/* + Copyright 2019 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 "MXStore.h" +#import "MXAggregationsStore.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MXAggregatedEditsUpdater : NSObject + +- (instancetype)initWithMyUser:(NSString*)userId + aggregationStore:(id)store + matrixStore:(id)matrixStore; + +//- (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId; +//- (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId; + +//- (id)listenToEditsUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; +//- (void)removeListener:(id)listener; + +- (void)handleReplace:(MXEvent *)event direction:(MXTimelineDirection)direction; +- (void)handleRedaction:(MXEvent *)event; + +- (void)resetDataInRoom:(NSString *)roomId; + +@end + +NS_ASSUME_NONNULL_END diff --git a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m new file mode 100644 index 0000000000..b03112ad4b --- /dev/null +++ b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m @@ -0,0 +1,67 @@ +/* + Copyright 2019 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 "MXAggregatedEditsUpdater.h" + +@interface MXAggregatedEditsUpdater () + +@property (nonatomic, weak) NSString *myUserId; +@property (nonatomic, weak) id matrixStore; +@property (nonatomic, weak) id store; +//@property (nonatomic) NSMutableArray *listeners; + +@end + +@implementation MXAggregatedEditsUpdater + +- (instancetype)initWithMyUser:(NSString*)userId + aggregationStore:(id)store + matrixStore:(id)matrixStore +{ + self = [super init]; + if (self) + { + self.myUserId = userId; + self.store = store; + self.matrixStore = matrixStore; + + //self.listeners = [NSMutableArray array]; + } + return self; +} + +//- (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId; +//- (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId; + +//- (id)listenToEditsUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; +//- (void)removeListener:(id)listener; + +- (void)handleReplace:(MXEvent *)event direction:(MXTimelineDirection)direction +{ + +} + +- (void)handleRedaction:(MXEvent *)event +{ + +} + +- (void)resetDataInRoom:(NSString *)roomId +{ + +} + +@end diff --git a/MatrixSDK/Aggregations/MXAggregations.h b/MatrixSDK/Aggregations/MXAggregations.h index cc96e1dd83..e73973cb20 100644 --- a/MatrixSDK/Aggregations/MXAggregations.h +++ b/MatrixSDK/Aggregations/MXAggregations.h @@ -21,6 +21,7 @@ #import "MXAggregatedReactions.h" #import "MXReactionCount.h" #import "MXReactionCountChange.h" +#import "MXEvent.h" NS_ASSUME_NONNULL_BEGIN @@ -98,6 +99,28 @@ NS_ASSUME_NONNULL_BEGIN - (void)removeListener:(id)listener; +#pragma mark - Edits + +/** + Replace a text in a matrix event. + + @param event The event to update + @param text The new message text + + @param success A block object called when the operation succeeds. It returns + the event id of the event generated on the homeserver. + @param failure A block object called when the operation fails. + + @return a MXHTTPOperation instance. + */ +- (MXHTTPOperation*)replaceTextMessageEvent:(MXEvent*)event + withTextMessage:(nullable NSString*)text +// formattedText:(nullable NSString*)formattedText // TODO +// localEcho:(MXEvent**)localEcho // TODO + success:(void (^)(NSString *eventId))success + failure:(void (^)(NSError *error))failure; + + /** Clear cached data. diff --git a/MatrixSDK/Aggregations/MXAggregations.m b/MatrixSDK/Aggregations/MXAggregations.m index 2d08ab7488..1fa3796925 100644 --- a/MatrixSDK/Aggregations/MXAggregations.m +++ b/MatrixSDK/Aggregations/MXAggregations.m @@ -25,13 +25,14 @@ #import "MXRealmAggregationsStore.h" #import "MXAggregatedReactionsUpdater.h" - +#import "MXAggregatedEditsUpdater.h" @interface MXAggregations () @property (nonatomic, weak) MXSession *mxSession; @property (nonatomic) id store; @property (nonatomic) MXAggregatedReactionsUpdater *aggregatedReactionsUpdater; +@property (nonatomic) MXAggregatedEditsUpdater *aggregatedEditsUpdater; @end @@ -67,6 +68,10 @@ - (MXHTTPOperation*)sendReaction:(NSString*)reaction { [self sendReactionUsingHack:reaction toEvent:eventId inRoom:roomId success:success failure:failure]; } + else + { + failure(error); + } }]; } @@ -122,6 +127,35 @@ - (void)resetData } +#pragma mark - Edits + +- (MXHTTPOperation*)replaceTextMessageEvent:(MXEvent*)event + withTextMessage:(nullable NSString*)text +// formattedText:(nullable NSString*)formattedText // TODO +// localEcho:(MXEvent**)localEcho // TODO + success:(void (^)(NSString *eventId))success + failure:(void (^)(NSError *error))failure; +{ + NSDictionary *content = @{ + @"msgtype": kMXMessageTypeText, + @"body": [NSString stringWithFormat:@"* %@", event.content[@"body"]], + @"m.new_content": @{ + @"msgtype": kMXMessageTypeText, + @"body": text + } + }; + + // TODO: manage a sent state like when using classic /send + return [self.mxSession.matrixRestClient sendRelationToEvent:event.eventId + inRoom:event.roomId + relationType:MXEventRelationTypeReplace + eventType:kMXEventTypeStringRoomMessage + parameters:nil + content:content + success:success failure:failure]; +} + + #pragma mark - SDK-Private methods - - (instancetype)initWithMatrixSession:(MXSession *)mxSession @@ -133,24 +167,13 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession self.store = [[MXRealmAggregationsStore alloc] initWithCredentials:mxSession.matrixRestClient.credentials]; self.aggregatedReactionsUpdater = [[MXAggregatedReactionsUpdater alloc] initWithMyUser:mxSession.matrixRestClient.credentials.userId - aggregationStore:self.store matrixStore:mxSession.store]; - - [mxSession listenToEventsOfTypes:@[kMXEventTypeStringReaction, kMXEventTypeStringRoomRedaction] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { - - switch (event.eventType) { - case MXEventTypeReaction: - [self.aggregatedReactionsUpdater handleReaction:event direction:direction]; - break; - case MXEventTypeRoomRedaction: - if (direction == MXTimelineDirectionForwards) - { - [self.aggregatedReactionsUpdater handleRedaction:event]; - } - break; - default: - break; - } - }]; + aggregationStore:self.store + matrixStore:mxSession.store]; + self.aggregatedEditsUpdater = [[MXAggregatedEditsUpdater alloc] initWithMyUser:mxSession.matrixRestClient.credentials.userId + aggregationStore:self.store + matrixStore:mxSession.store]; + + [self registerListener]; } return self; @@ -161,6 +184,36 @@ - (void)resetDataInRoom:(NSString *)roomId [self.aggregatedReactionsUpdater resetDataInRoom:roomId]; } + +#pragma mark - Private methods + +- (void)registerListener +{ + [self.mxSession listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage, kMXEventTypeStringReaction, kMXEventTypeStringRoomRedaction] onEvent:^(MXEvent *event, MXTimelineDirection direction, id customObject) { + + switch (event.eventType) { + case MXEventTypeRoomMessage: + if ([event.relatesTo.relationType isEqualToString:MXEventRelationTypeReplace]) + { + [self.aggregatedEditsUpdater handleReplace:event direction:direction]; + } + break; + case MXEventTypeReaction: + [self.aggregatedReactionsUpdater handleReaction:event direction:direction]; + break; + case MXEventTypeRoomRedaction: + if (direction == MXTimelineDirectionForwards) + { + [self.aggregatedReactionsUpdater handleRedaction:event]; + } + break; + default: + break; + } + }]; +} + + #pragma mark - Reactions hack (TODO: Remove all methods) - /// TODO: To remove once the feature has landed on matrix.org homeserver diff --git a/MatrixSDKTests/MXAggregatedEditsTests.m b/MatrixSDKTests/MXAggregatedEditsTests.m new file mode 100644 index 0000000000..1faddd9566 --- /dev/null +++ b/MatrixSDKTests/MXAggregatedEditsTests.m @@ -0,0 +1,187 @@ +/* + * Copyright 2019 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 "MatrixSDKTestsData.h" + +#import "MXFileStore.h" + +// Do not bother with retain cycles warnings in tests +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-retain-cycles" + +@interface MXAggregatedEditsTests : XCTestCase +{ + MatrixSDKTestsData *matrixSDKTestsData; +} + +@end + +@implementation MXAggregatedEditsTests + +- (void)setUp +{ + [super setUp]; + + matrixSDKTestsData = [[MatrixSDKTestsData alloc] init]; +} + +- (void)tearDown +{ + matrixSDKTestsData = nil; +} + +// Create a room with an event with an edit it on it +- (void)createScenario:(void(^)(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *editEventId))readyToTest +{ + [matrixSDKTestsData doMXSessionTestWithBobAndARoom:self andStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation) { + + [room sendTextMessage:@"Bonjour" success:^(NSString *eventId) { + [mxSession eventWithEventId:eventId inRoom:room.roomId success:^(MXEvent *event) { + [mxSession.aggregations replaceTextMessageEvent:event withTextMessage:@"I meant Hello" success:^(NSString * _Nonnull editEventId) { + + // TODO: replaceTextMessageEvent should return only when the actual edit event comes back the sync + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ + readyToTest(mxSession, room, expectation, eventId, editEventId); + }); + + } failure:^(NSError *error) { + XCTFail(@"The operation should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + + +// - Send a message +// - Edit it +// -> an edit m.room.message must appear in the timeline +- (void)testEditSendAndReceive +{ + [matrixSDKTestsData doMXSessionTestWithBobAndARoom:self andStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation) { + + // - Send a message + [room sendTextMessage:@"Bonjour" success:^(NSString *eventId) { + + [mxSession eventWithEventId:eventId inRoom:room.roomId success:^(MXEvent *event) { + + // Edit it + [mxSession.aggregations replaceTextMessageEvent:event withTextMessage:@"I meant Hello" success:^(NSString * _Nonnull eventId) { + XCTAssertNotNil(eventId); + } failure:^(NSError *error) { + XCTFail(@"The operation should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + [room listenToEventsOfTypes:@[kMXEventTypeStringRoomMessage] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) { + + // -> an edit m.room.message must appear in the timeline + XCTAssertEqual(event.eventType, MXEventTypeRoomMessage); + + XCTAssertNotNil(event.relatesTo); + XCTAssertEqualObjects(event.relatesTo.relationType, MXEventRelationTypeReplace); + XCTAssertEqualObjects(event.relatesTo.eventId, eventId); + + XCTAssertEqualObjects(event.content[@"m.new_content"][@"msgtype"], kMXMessageTypeText); + XCTAssertEqualObjects(event.content[@"m.new_content"][@"body"], @"I meant Hello"); + + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + +// - Run the initial condition scenario +// -> Check data is correctly aggregated when fetching the edited event directly from the homeserver +- (void)testAggregatedEditServerSide +{ + // - Run the initial condition scenario + [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *editEventId) { + + // -> Check data is correctly aggregated when fetching the edited event directly from the homeserver + [mxSession.matrixRestClient eventWithEventId:eventId inRoom:room.roomId success:^(MXEvent *event) { + + XCTAssertNotNil(event.unsignedData.relations); + + XCTFail(@"TODO: Write the test once we know what we should receive"); + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + +// - Run the initial condition scenario +// - Do an initial sync +// -> Data from aggregations must be right +- (void)testEditsFromInitialSync +{ + // - Run the initial condition scenario + [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *editEventId) { + + MXRestClient *restClient = mxSession.matrixRestClient; + + [mxSession.aggregations resetData]; + [mxSession close]; + mxSession = nil; + + // - Do an initial sync + mxSession = [[MXSession alloc] initWithMatrixRestClient:restClient]; + [mxSession setStore:[[MXMemoryStore alloc] init] success:^{ + + [mxSession start:^{ + + // -> Data from aggregations must be right + XCTFail(@"TODO: Write the test once we know what we should receive"); + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + +@end + +#pragma clang diagnostic pop diff --git a/MatrixSDKTests/MXAggregatedReactionTests.m b/MatrixSDKTests/MXAggregatedReactionTests.m index 50e176ee23..96b5f09165 100644 --- a/MatrixSDKTests/MXAggregatedReactionTests.m +++ b/MatrixSDKTests/MXAggregatedReactionTests.m @@ -113,6 +113,36 @@ - (void)testReactionSendAndReceive }]; } +// - Run the initial condition scenario +// -> Check data is correctly aggregated when fetching the reacted event directly from the homeserver +- (void)testAggregatedReaction +{ + // - Run the initial condition scenario + [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *editEventId) { + + // -> Check data is correctly aggregated when fetching the reacted event directly from the homeserver + [mxSession.matrixRestClient eventWithEventId:eventId inRoom:room.roomId success:^(MXEvent *event) { + + MXEventAnnotationChunk *annotations = event.unsignedData.relations.annotation; + XCTAssertNotNil(annotations); + XCTAssertEqual(annotations.count, 1); + XCTAssertEqual(annotations.chunk.count, 1); + + MXEventAnnotation *annotation = annotations.chunk.firstObject; + XCTAssertNotNil(annotation); + XCTAssertEqualObjects(annotation.type, MXEventAnnotationReaction); + XCTAssertEqualObjects(annotation.key, @"👍"); + XCTAssertEqual(annotation.count, 1); + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + // - Run the initial condition scenario // - Do an initial sync // -> Data from aggregations must be right @@ -255,6 +285,8 @@ - (void)testUnreact }]; } + + @end #pragma clang diagnostic pop From 6e5ff0d9a1b36ea27eda8e29e43ee5d004a3854b Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 23 May 2019 22:18:55 +0200 Subject: [PATCH 07/15] Reactions: More tests --- MatrixSDKTests/MXAggregatedReactionTests.m | 145 +++++++++++++++++++-- MatrixSDKTests/MatrixSDKTestsData.h | 5 + MatrixSDKTests/MatrixSDKTestsData.m | 25 ++++ 3 files changed, 167 insertions(+), 8 deletions(-) diff --git a/MatrixSDKTests/MXAggregatedReactionTests.m b/MatrixSDKTests/MXAggregatedReactionTests.m index 96b5f09165..099e92d634 100644 --- a/MatrixSDKTests/MXAggregatedReactionTests.m +++ b/MatrixSDKTests/MXAggregatedReactionTests.m @@ -51,17 +51,18 @@ - (void)tearDown } // Create a room with an event with a reaction on it -- (void)createScenario:(void(^)(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId))readyToTest +- (void)createScenario:(void(^)(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId))readyToTest { - [matrixSDKTestsData doMXSessionTestWithBobAndARoom:self andStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation) { + [matrixSDKTestsData doTestWithAliceAndBobInARoom:self aliceStore:[[MXMemoryStore alloc] init] bobStore:[[MXMemoryStore alloc] init] readyToTest:^(MXSession *mxSession, MXSession *otherSession, NSString *roomId, XCTestExpectation *expectation) { + MXRoom *room = [mxSession roomWithRoomId:roomId]; [room sendTextMessage:@"Hello" success:^(NSString *eventId) { [mxSession.aggregations sendReaction:@"👍" toEvent:eventId inRoom:room.roomId success:^(NSString *reactionEventId) { // TODO: sendReaction should return only when the actual reaction event comes back the sync dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ - readyToTest(mxSession, room, expectation, eventId, reactionEventId); + readyToTest(mxSession, room, otherSession, expectation, eventId, reactionEventId); }); } failure:^(NSError *error) { @@ -75,6 +76,73 @@ - (void)createScenario:(void(^)(MXSession *mxSession, MXRoom *room, XCTestExpect }]; } +// - Create a room with an event with a reaction on it +// - Add enough messages while the session in background to trigger a gappy sync +// - Add a reaction in the gap +- (void)createScenarioWithAGappySync:(void(^)(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId))readyToTest +{ + // - Create a room with an event with a reaction on it + [self createScenario:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + + // - Add enough messages while the session in background to trigger a gappy sync + [mxSession pause]; + [matrixSDKTestsData for:mxSession.matrixRestClient andRoom:room.roomId sendMessages:10 success:^{ + + // - Add a reaction in the gap + [otherSession.aggregations sendReaction:@"🙂" toEvent:eventId inRoom:room.roomId success:^(NSString * _Nonnull reactionEventId2) { + + [matrixSDKTestsData for:mxSession.matrixRestClient andRoom:room.roomId sendMessages:20 success:^{ + + [mxSession start:^{ + readyToTest(mxSession, room, otherSession, expectation, eventId, reactionEventId); + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + + }]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; + }]; +} + +// - Create a room with an event with a reaction on it +// - Add enough messages while the session in background to trigger a gappy sync +// - Add a reaction in the gap +// - Do an initial sync +- (void)createScenarioWithAGappyInitialSync:(void(^)(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId))readyToTest +{ + [self createScenarioWithAGappySync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + + MXRestClient *restClient = mxSession.matrixRestClient; + NSString *roomId = room.roomId; + + [mxSession.aggregations resetData]; + [mxSession close]; + + // - Do an initial sync + MXSession *mxSession2 = [[MXSession alloc] initWithMatrixRestClient:restClient]; + [mxSession2 setStore:[[MXMemoryStore alloc] init] success:^{ + + [mxSession2 start:^{ + readyToTest(mxSession2, [mxSession2 roomWithRoomId:roomId], otherSession, expectation, eventId, reactionEventId); + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} + + // - Send a message // - React on it // -> a m.reaction message must appear in the timeline @@ -118,7 +186,7 @@ - (void)testReactionSendAndReceive - (void)testAggregatedReaction { // - Run the initial condition scenario - [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *editEventId) { + [self createScenario:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *editEventId) { // -> Check data is correctly aggregated when fetching the reacted event directly from the homeserver [mxSession.matrixRestClient eventWithEventId:eventId inRoom:room.roomId success:^(MXEvent *event) { @@ -149,7 +217,7 @@ - (void)testAggregatedReaction - (void)testAggregationsFromInitialSync { // - Run the initial condition scenario - [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + [self createScenario:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { MXRestClient *restClient = mxSession.matrixRestClient; @@ -193,7 +261,7 @@ - (void)testAggregationsFromInitialSync - (void)testAggregationsLive { // - Run the initial condition scenario - [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + [self createScenario:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { // -> Data from aggregations must be right MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; @@ -216,7 +284,7 @@ - (void)testAggregationsLive - (void)testAggregationsListener { // - Run the initial condition scenario - [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + [self createScenario:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { // -> We must get notified about the reaction count change [mxSession.aggregations listenToReactionCountUpdateInRoom:room.roomId block:^(NSDictionary * _Nonnull changes) { @@ -253,7 +321,7 @@ - (void)testAggregationsListener - (void)testUnreact { // - Run the initial condition scenario - [self createScenario:^(MXSession *mxSession, MXRoom *room, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + [self createScenario:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { // -> We must get notified about the reaction count change [mxSession.aggregations listenToReactionCountUpdateInRoom:room.roomId block:^(NSDictionary * _Nonnull changes) { @@ -286,6 +354,67 @@ - (void)testUnreact } +- (void)checkReactionsWhenPaginating:(MXSession*)mxSession room:(MXRoom*)room event:(NSString*)eventId expectation:(XCTestExpectation*)expectation +{ + // TODO + // MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; + // XCTAssertNotNil(reactions, @"TODO: The code should not forget reactions"); + // XCTAssertEqualObjects(reactions.reactions.firstObject.reaction, @"👍"); + + [room liveTimeline:^(MXEventTimeline *liveTimeline) { + [liveTimeline resetPagination]; + [liveTimeline paginate:100 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ + + // -> Data from aggregations must be right + MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; + + XCTAssertNotNil(reactions.reactions); + XCTAssertEqual(reactions.reactions.count, 2); + + for (MXReactionCount *reactionCount in reactions.reactions) + { + XCTAssertEqual(reactionCount.count, 1); + if ([reactionCount.reaction isEqualToString: @"👍"]) + { + XCTAssertTrue(reactionCount.myUserHasReacted, @"We must know reaction made by our user"); + } + else if ([reactionCount.reaction isEqualToString: @"🙂"]) + { + XCTAssertFalse(reactionCount.myUserHasReacted); + } + else + { + XCTFail(@"Unexpected reaction: %@ in reactions: %@", reactionCount, reactions.reactions); + } + } + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"The operation should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + }]; +} + +- (void)testReactionsWhenPaginatingFromAGappySync +{ + [self createScenarioWithAGappySync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + + [self checkReactionsWhenPaginating:mxSession room:room event:eventId expectation:expectation]; + }]; +} + +- (void)testReactionsWhenPaginatingFromAGappyInitialSync +{ + // TODO: reactionCount.myUserHasReacted fails because of spec + // https://github.com/vector-im/riot-ios/issues/2452 + [self createScenarioWithAGappyInitialSync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + + [self checkReactionsWhenPaginating:mxSession room:room event:eventId expectation:expectation]; + }]; +} + @end diff --git a/MatrixSDKTests/MatrixSDKTestsData.h b/MatrixSDKTests/MatrixSDKTestsData.h index 469ced2104..19581c3df8 100644 --- a/MatrixSDKTests/MatrixSDKTestsData.h +++ b/MatrixSDKTests/MatrixSDKTestsData.h @@ -116,6 +116,11 @@ FOUNDATION_EXPORT NSString * const kMXTestsAliceAvatarURL; andStore:(id)bobStore readyToTest:(void (^)(MXSession *bobSession, MXRestClient *aliceRestClient, NSString* roomId, XCTestExpectation *expectation))readyToTest; +- (void)doTestWithAliceAndBobInARoom:(XCTestCase*)testCase + aliceStore:(id)aliceStore + bobStore:(id)bobStore + readyToTest:(void (^)(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation))readyToTest; + #pragma mark - random user - (void)doMXSessionTestWithAUser:(XCTestCase*)testCase diff --git a/MatrixSDKTests/MatrixSDKTestsData.m b/MatrixSDKTests/MatrixSDKTestsData.m index 0748b8f78d..652f059e12 100644 --- a/MatrixSDKTests/MatrixSDKTestsData.m +++ b/MatrixSDKTests/MatrixSDKTestsData.m @@ -653,6 +653,31 @@ - (void)doMXSessionTestWithBobAndAliceInARoom:(XCTestCase*)testCase }]; } +- (void)doTestWithAliceAndBobInARoom:(XCTestCase*)testCase + aliceStore:(id)aliceStore + bobStore:(id)bobStore + readyToTest:(void (^)(MXSession *aliceSession, MXSession *bobSession, NSString *roomId, XCTestExpectation *expectation))readyToTest +{ + [self doMXSessionTestWithBobAndAliceInARoom:testCase andStore:bobStore readyToTest:^(MXSession *bobSession, MXRestClient *aliceRestClient, NSString *roomId, XCTestExpectation *expectation) { + + MXSession *aliceSession = [[MXSession alloc] initWithMatrixRestClient:aliceRestClient]; + [self retain:aliceSession]; + + [aliceSession setStore:aliceStore success:^{ + + [aliceSession start:^{ + + readyToTest(aliceSession, bobSession, roomId, expectation); + + } failure:^(NSError *error) { + NSAssert(NO, @"Cannot set up intial test conditions - error: %@", error); + }]; + } failure:^(NSError *error) { + NSAssert(NO, @"Cannot set up intial test conditions - error: %@", error); + }]; + }]; +} + #pragma mark - random user - (void)doMXSessionTestWithAUser:(XCTestCase*)testCase From af95be2965f463751befb28f966ecb24934b9ff2 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 23 May 2019 22:48:52 +0200 Subject: [PATCH 08/15] Reactions: More tests --- MatrixSDKTests/MXAggregatedReactionTests.m | 87 +++++++++++++++++----- 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/MatrixSDKTests/MXAggregatedReactionTests.m b/MatrixSDKTests/MXAggregatedReactionTests.m index 099e92d634..3389af0d83 100644 --- a/MatrixSDKTests/MXAggregatedReactionTests.m +++ b/MatrixSDKTests/MXAggregatedReactionTests.m @@ -354,6 +354,33 @@ - (void)testUnreact } +#pragma mark - Pagination + +- (void)checkGappySyncScenarionReactions:(MXAggregatedReactions*)reactions +{ + XCTAssertNotNil(reactions.reactions); + XCTAssertEqual(reactions.reactions.count, 2); + + for (MXReactionCount *reactionCount in reactions.reactions) + { + XCTAssertEqual(reactionCount.count, 1); + if ([reactionCount.reaction isEqualToString: @"👍"]) + { + XCTAssertTrue(reactionCount.myUserHasReacted, @"We must know reaction made by our user"); + } + else if ([reactionCount.reaction isEqualToString: @"🙂"]) + { + XCTAssertFalse(reactionCount.myUserHasReacted); + } + else + { + XCTFail(@"Unexpected reaction: %@ in reactions: %@", reactionCount, reactions.reactions); + } + } +} + + +// Check we get valid reaction (from the HS) when paginating - (void)checkReactionsWhenPaginating:(MXSession*)mxSession room:(MXRoom*)room event:(NSString*)eventId expectation:(XCTestExpectation*)expectation { // TODO @@ -367,26 +394,7 @@ - (void)checkReactionsWhenPaginating:(MXSession*)mxSession room:(MXRoom*)room ev // -> Data from aggregations must be right MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; - - XCTAssertNotNil(reactions.reactions); - XCTAssertEqual(reactions.reactions.count, 2); - - for (MXReactionCount *reactionCount in reactions.reactions) - { - XCTAssertEqual(reactionCount.count, 1); - if ([reactionCount.reaction isEqualToString: @"👍"]) - { - XCTAssertTrue(reactionCount.myUserHasReacted, @"We must know reaction made by our user"); - } - else if ([reactionCount.reaction isEqualToString: @"🙂"]) - { - XCTAssertFalse(reactionCount.myUserHasReacted); - } - else - { - XCTFail(@"Unexpected reaction: %@ in reactions: %@", reactionCount, reactions.reactions); - } - } + [self checkGappySyncScenarionReactions:reactions]; [expectation fulfill]; @@ -416,6 +424,45 @@ - (void)testReactionsWhenPaginatingFromAGappyInitialSync } +#pragma mark - Permalink + +// Check we get valid reaction (from the HS) when paginating +- (void)checkReactionsOnPermalink:(MXSession*)mxSession room:(MXRoom*)room event:(NSString*)eventId expectation:(XCTestExpectation*)expectation +{ + MXEventTimeline *timeline = [room timelineOnEvent:eventId]; + [timeline resetPagination]; + [timeline paginate:5 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ + + // Random usage to keep a strong reference on timeline + [timeline resetPagination]; + + MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; + [self checkGappySyncScenarionReactions:reactions]; + + [expectation fulfill]; + + } failure:^(NSError *error) { + XCTFail(@"The operation should not fail - NSError: %@", error); + [expectation fulfill]; + }]; +} + +- (void)testReactionsOnPermalinkFromAGappySync +{ + [self createScenarioWithAGappySync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + + [self checkReactionsOnPermalink:mxSession room:room event:eventId expectation:expectation]; + }]; +} + +- (void)testReactionsOnPermalinkFromAGappyInitialSync +{ + [self createScenarioWithAGappyInitialSync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + + [self checkReactionsOnPermalink:mxSession room:room event:eventId expectation:expectation]; + }]; +} + @end #pragma clang diagnostic pop From 39cd1a84b87de9beeaa409ab839dc1defd75b364 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 24 May 2019 06:17:51 +0200 Subject: [PATCH 09/15] Reactions: Test for "Unreact can have no visual effect" (https://github.com/vector-im/riot-ios/issues/2470) --- MatrixSDKTests/MXAggregatedReactionTests.m | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/MatrixSDKTests/MXAggregatedReactionTests.m b/MatrixSDKTests/MXAggregatedReactionTests.m index 3389af0d83..ce56722e41 100644 --- a/MatrixSDKTests/MXAggregatedReactionTests.m +++ b/MatrixSDKTests/MXAggregatedReactionTests.m @@ -353,6 +353,60 @@ - (void)testUnreact }]; } +// Test for "Unreact can have no visual effect" (https://github.com/vector-im/riot-ios/issues/2470) +// - Run the initial condition scenario +// - Do an initial sync +// - Unreact +// -> Data from aggregations must be right +- (void)testUnreactAfterInitialSync +{ + // - Run the initial condition scenario + [self createScenario:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + + MXRestClient *restClient = mxSession.matrixRestClient; + + [mxSession.aggregations resetData]; + [mxSession close]; + mxSession = nil; + + // - Do an initial sync + mxSession = [[MXSession alloc] initWithMatrixRestClient:restClient]; + [mxSession setStore:[[MXMemoryStore alloc] init] success:^{ + + [mxSession start:^{ + + [mxSession.aggregations listenToReactionCountUpdateInRoom:room.roomId block:^(NSDictionary * _Nonnull changes) { + + XCTAssertEqual(changes.count, 1, @"Only one change"); + + MXReactionCountChange *change = changes[eventId]; + XCTAssertNotNil(change.deleted); + + // -> Data from aggregations must be right + MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; + XCTAssertNil(reactions); + + [expectation fulfill]; + }]; + + // - Unreact + [mxSession.aggregations unReactOnReaction:@"👍" toEvent:eventId inRoom:room.roomId success:^() { + } failure:^(NSError *error) { + XCTFail(@"The operation should not fail - NSError: %@", error); + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + + } failure:^(NSError *error) { + XCTFail(@"Cannot set up intial test conditions - error: %@", error); + [expectation fulfill]; + }]; + }]; +} #pragma mark - Pagination From dc7a89b15d51b309f95c1bc443d55c4e104ca449 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 24 May 2019 07:34:17 +0200 Subject: [PATCH 10/15] Reactions: Have a single source of truth for aggregated data, the aggregation store. Every event received in timelines (live or not) is now processed by MXAggregation. This fixes issues like "Unreact can have no visual effect" (https://github.com/vector-im/riot-ios/issues/2470) --- .../MXAggregatedReactionsUpdater.h | 2 + .../MXAggregatedReactionsUpdater.m | 97 +++++++++---------- MatrixSDK/Aggregations/MXAggregations.m | 13 +++ .../Aggregations/MXAggregations_Private.h | 10 +- MatrixSDK/Data/MXEventTimeline.m | 13 +++ 5 files changed, 81 insertions(+), 54 deletions(-) diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h index eefd1bcf8e..6b15da28e7 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h @@ -17,6 +17,7 @@ #import #import "MXAggregatedReactions.h" +#import "MXEventAnnotationChunk.h" #import "MXStore.h" #import "MXAggregationsStore.h" @@ -36,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN - (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; - (void)removeListener:(id)listener; +- (void)handleOriginalAggregatedDataOfEvent:(MXEvent *)event annotations:(MXEventAnnotationChunk*)annotations; - (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction; - (void)handleRedaction:(MXEvent *)event; diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m index 769c518b34..15e01d834d 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m @@ -49,10 +49,6 @@ - (instancetype)initWithMyUser:(NSString *)userId aggregationStore:(id *reactions = [self.store reactionCountsOnEvent:eventId]; - if (!reactions) - { - reactions = [self reactionCountsFromMatrixStoreOnEvent:eventId inRoom:roomId]; - } if (!reactions) { @@ -91,6 +87,32 @@ - (void)removeListener:(id)listener } +- (void)handleOriginalAggregatedDataOfEvent:(MXEvent *)event annotations:(MXEventAnnotationChunk*)annotations +{ + NSMutableArray *reactions; + + for (MXEventAnnotation *annotation in annotations.chunk) + { + if ([annotation.type isEqualToString:MXEventAnnotationReaction]) + { + MXReactionCount *reactionCount = [MXReactionCount new]; + reactionCount.reaction = annotation.key; + reactionCount.count = annotation.count; + + if (!reactions) + { + reactions = [NSMutableArray array]; + } + [reactions addObject:reactionCount]; + } + } + + if (reactions) + { + [self.store setReactionCounts:reactions onEvent:event.eventId inRoom:event.roomId]; + } +} + - (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction { @@ -156,7 +178,7 @@ - (void)updateReactionCountForReaction:(NSString*)reaction toEvent:(NSString*)ev BOOL isANewReaction = NO; // Migrate data from matrix store to aggregation store if needed - [self checkAggregationStoreForEvent:eventId inRoomId:reactionEvent.roomId]; + [self checkAggregationStoreWithHackForEvent:eventId inRoomId:reactionEvent.roomId]; // Create or update the current reaction count if it exists MXReactionCount *reactionCount = [self.store reactionCountForReaction:reaction onEvent:eventId]; @@ -190,7 +212,7 @@ - (void)updateReactionCountForReaction:(NSString*)reaction toEvent:(NSString*)ev - (void)removeReaction:(NSString*)reaction onEvent:(NSString*)eventId inRoomId:(NSString*)roomId { // Migrate data from matrix store to aggregation store if needed - [self checkAggregationStoreForEvent:eventId inRoomId:roomId]; + [self checkAggregationStoreWithHackForEvent:eventId inRoomId:roomId]; // Create or update the current reaction count if it exists MXReactionCount *reactionCount = [self.store reactionCountForReaction:reaction onEvent:eventId]; @@ -214,53 +236,6 @@ - (void)removeReaction:(NSString*)reaction onEvent:(NSString*)eventId inRoomId:( } } -// If not already done, copy aggregation data from matrix store to aggregation store -- (void)checkAggregationStoreForEvent:(NSString*)eventId inRoomId:(NSString*)roomId -{ - if (![self.store hasReactionCountsOnEvent:eventId]) - { - NSArray *reactions = [self reactionCountsFromMatrixStoreOnEvent:eventId inRoom:roomId]; - - if (!reactions) - { - // Check reaction data from the hack - reactions = [self reactionCountsUsingHackOnEvent:eventId inRoom:roomId]; - } - - if (reactions) - { - [self.store setReactionCounts:reactions onEvent:eventId inRoom:roomId]; - } - } -} - -- (nullable NSArray *)reactionCountsFromMatrixStoreOnEvent:(NSString*)eventId inRoom:(NSString*)roomId -{ - NSMutableArray *reactions; - - MXEvent *event = [self.matrixStore eventWithEventId:eventId inRoom:roomId]; - if (event) - { - for (MXEventAnnotation *annotation in event.unsignedData.relations.annotation.chunk) - { - if ([annotation.type isEqualToString:MXEventAnnotationReaction]) - { - MXReactionCount *reactionCount = [MXReactionCount new]; - reactionCount.reaction = annotation.key; - reactionCount.count = annotation.count; - - if (!reactions) - { - reactions = [NSMutableArray array]; - } - [reactions addObject:reactionCount]; - } - } - } - - return reactions; -} - - (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId event:(NSString*)eventId reactionCount:(MXReactionCount*)reactionCount isNewReaction:(BOOL)isNewReaction { MXReactionCountChange *reactionCountChange = [MXReactionCountChange new]; @@ -299,10 +274,26 @@ - (void)notifyReactionCountChangeListenersOfRoom:(NSString*)roomId changes:(NSDi } } + #pragma mark - Reactions hack (TODO: Remove all methods) - /// TODO: To remove once the feature has landed on matrix.org homeserver +// If not already done, run the hack: build reaction count from known relations +- (void)checkAggregationStoreWithHackForEvent:(NSString*)eventId inRoomId:(NSString*)roomId +{ + if (![self.store hasReactionCountsOnEvent:eventId]) + { + // Check reaction data from the hack + NSArray *reactions = [self reactionCountsUsingHackOnEvent:eventId inRoom:roomId]; + + if (reactions) + { + [self.store setReactionCounts:reactions onEvent:eventId inRoom:roomId]; + } + } +} + // Compute reactions counts from relations we know // Note: This is not accurate and will be removed soon - (nullable NSArray *)reactionCountsUsingHackOnEvent:(NSString*)eventId inRoom:(NSString*)roomId diff --git a/MatrixSDK/Aggregations/MXAggregations.m b/MatrixSDK/Aggregations/MXAggregations.m index 1fa3796925..333f74b9df 100644 --- a/MatrixSDK/Aggregations/MXAggregations.m +++ b/MatrixSDK/Aggregations/MXAggregations.m @@ -179,6 +179,19 @@ - (instancetype)initWithMatrixSession:(MXSession *)mxSession return self; } +- (void)handleOriginalDataOfEvent:(MXEvent *)event +{ + MXEventRelations *relations = event.unsignedData.relations; + if (relations.annotation) + { + [self.aggregatedReactionsUpdater handleOriginalAggregatedDataOfEvent:event annotations:relations.annotation]; + } + // else if (relations.replace) + // { + // // TODO: Manage edits + // } +} + - (void)resetDataInRoom:(NSString *)roomId { [self.aggregatedReactionsUpdater resetDataInRoom:roomId]; diff --git a/MatrixSDK/Aggregations/MXAggregations_Private.h b/MatrixSDK/Aggregations/MXAggregations_Private.h index 8d71032483..fb432cb10e 100644 --- a/MatrixSDK/Aggregations/MXAggregations_Private.h +++ b/MatrixSDK/Aggregations/MXAggregations_Private.h @@ -16,7 +16,7 @@ #import "MXAggregations.h" -@class MXSession; +@class MXSession, MXEvent; NS_ASSUME_NONNULL_BEGIN @@ -33,6 +33,14 @@ NS_ASSUME_NONNULL_BEGIN */ - (instancetype)initWithMatrixSession:(MXSession *)mxSession; +/** + Notify the aggregation manager for every events so that it can store + aggregated data sent by the server. + + @param event the event received in a timeline. + */ +- (void)handleOriginalDataOfEvent:(MXEvent*)event; + /** Clear cached data for a room. diff --git a/MatrixSDK/Data/MXEventTimeline.m b/MatrixSDK/Data/MXEventTimeline.m index fad6a7109a..57ecfebd8a 100644 --- a/MatrixSDK/Data/MXEventTimeline.m +++ b/MatrixSDK/Data/MXEventTimeline.m @@ -627,6 +627,19 @@ - (void)addEvent:(MXEvent*)event direction:(MXTimelineDirection)direction fromSt [store storeEventForRoom:_state.roomId event:event direction:direction]; } + // Notify the aggregation manager for every events so that it can store + // aggregated data sent by the server + + // TODO: remove this check once https://github.com/matrix-org/synapse/pull/5220 is merged + if (_isLiveTimeline && direction == MXTimelineDirectionForwards && isRoomInitialSync) + { + // Do nothing to avoid double counting + } + else + { + [room.mxSession.aggregations handleOriginalDataOfEvent:event]; + } + // Notify listeners [self notifyListeners:event direction:direction]; } From 6d11aea93b00a5445794ebe7eb74de912e345f1b Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 24 May 2019 08:05:57 +0200 Subject: [PATCH 11/15] Reactions: Do not reset anymore aggregation data to make permalink work This fixes https://github.com/vector-im/riot-ios/issues/2451 --- MatrixSDK/Data/MXEventTimeline.m | 3 --- MatrixSDKTests/MXAggregatedReactionTests.m | 25 +++++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/MatrixSDK/Data/MXEventTimeline.m b/MatrixSDK/Data/MXEventTimeline.m index 57ecfebd8a..ae8b7bd2a7 100644 --- a/MatrixSDK/Data/MXEventTimeline.m +++ b/MatrixSDK/Data/MXEventTimeline.m @@ -444,9 +444,6 @@ - (void)handleJoinedRoomSync:(MXRoomSync *)roomSync { // Flush the existing messages for this room by keeping state events. [store deleteAllMessagesInRoom:_state.roomId]; - - // Flush aggregated data for the events in the timeline - [room.mxSession.aggregations resetDataInRoom:_state.roomId]; } for (MXEvent *event in roomSync.timeline.events) diff --git a/MatrixSDKTests/MXAggregatedReactionTests.m b/MatrixSDKTests/MXAggregatedReactionTests.m index ce56722e41..364f1dc201 100644 --- a/MatrixSDKTests/MXAggregatedReactionTests.m +++ b/MatrixSDKTests/MXAggregatedReactionTests.m @@ -193,7 +193,7 @@ - (void)testAggregatedReaction MXEventAnnotationChunk *annotations = event.unsignedData.relations.annotation; XCTAssertNotNil(annotations); - XCTAssertEqual(annotations.count, 1); + XCTAssertEqual(annotations.count, 1); // TODO: Ping Synapse team about that XCTAssertEqual(annotations.chunk.count, 1); MXEventAnnotation *annotation = annotations.chunk.firstObject; @@ -420,6 +420,7 @@ - (void)checkGappySyncScenarionReactions:(MXAggregatedReactions*)reactions XCTAssertEqual(reactionCount.count, 1); if ([reactionCount.reaction isEqualToString: @"👍"]) { + // TODO: https://github.com/vector-im/riot-ios/issues/2452 XCTAssertTrue(reactionCount.myUserHasReacted, @"We must know reaction made by our user"); } else if ([reactionCount.reaction isEqualToString: @"🙂"]) @@ -437,11 +438,6 @@ - (void)checkGappySyncScenarionReactions:(MXAggregatedReactions*)reactions // Check we get valid reaction (from the HS) when paginating - (void)checkReactionsWhenPaginating:(MXSession*)mxSession room:(MXRoom*)room event:(NSString*)eventId expectation:(XCTestExpectation*)expectation { - // TODO - // MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; - // XCTAssertNotNil(reactions, @"TODO: The code should not forget reactions"); - // XCTAssertEqualObjects(reactions.reactions.firstObject.reaction, @"👍"); - [room liveTimeline:^(MXEventTimeline *liveTimeline) { [liveTimeline resetPagination]; [liveTimeline paginate:100 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ @@ -463,14 +459,18 @@ - (void)testReactionsWhenPaginatingFromAGappySync { [self createScenarioWithAGappySync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + // While we have not yet paginated back, we should see an outdated reaction count + MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; + XCTAssertNotNil(reactions); + XCTAssertEqual(reactions.reactions.count, 1); + XCTAssertEqualObjects(reactions.reactions.firstObject.reaction, @"👍"); + [self checkReactionsWhenPaginating:mxSession room:room event:eventId expectation:expectation]; }]; } - (void)testReactionsWhenPaginatingFromAGappyInitialSync { - // TODO: reactionCount.myUserHasReacted fails because of spec - // https://github.com/vector-im/riot-ios/issues/2452 [self createScenarioWithAGappyInitialSync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { [self checkReactionsWhenPaginating:mxSession room:room event:eventId expectation:expectation]; @@ -484,8 +484,7 @@ - (void)testReactionsWhenPaginatingFromAGappyInitialSync - (void)checkReactionsOnPermalink:(MXSession*)mxSession room:(MXRoom*)room event:(NSString*)eventId expectation:(XCTestExpectation*)expectation { MXEventTimeline *timeline = [room timelineOnEvent:eventId]; - [timeline resetPagination]; - [timeline paginate:5 direction:MXTimelineDirectionBackwards onlyFromStore:NO complete:^{ + [timeline resetPaginationAroundInitialEventWithLimit:0 success:^{ // Random usage to keep a strong reference on timeline [timeline resetPagination]; @@ -505,6 +504,12 @@ - (void)testReactionsOnPermalinkFromAGappySync { [self createScenarioWithAGappySync:^(MXSession *mxSession, MXRoom *room, MXSession *otherSession, XCTestExpectation *expectation, NSString *eventId, NSString *reactionEventId) { + // While we have not yet paginated back, we should see an outdated reaction count + MXAggregatedReactions *reactions = [mxSession.aggregations aggregatedReactionsOnEvent:eventId inRoom:room.roomId]; + XCTAssertNotNil(reactions); + XCTAssertEqual(reactions.reactions.count, 1); + XCTAssertEqualObjects(reactions.reactions.firstObject.reaction, @"👍"); + [self checkReactionsOnPermalink:mxSession room:room event:eventId expectation:expectation]; }]; } From aabcd9e368d8cb2163fb205561f9c6562d2bfb0c Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 24 May 2019 08:10:31 +0200 Subject: [PATCH 12/15] Reactions: implement [MXRealmAggregationsStore setReactionCounts:] correctly --- .../Data/Store/Realm/MXRealmAggregationsStore.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MatrixSDK/Aggregations/Data/Store/Realm/MXRealmAggregationsStore.m b/MatrixSDK/Aggregations/Data/Store/Realm/MXRealmAggregationsStore.m index 06c260b867..a640e4d9d2 100644 --- a/MatrixSDK/Aggregations/Data/Store/Realm/MXRealmAggregationsStore.m +++ b/MatrixSDK/Aggregations/Data/Store/Realm/MXRealmAggregationsStore.m @@ -100,6 +100,12 @@ - (void)setReactionCounts:(nonnull NSArray *)reactionCounts o RLMRealm *realm = self.realm; [realm transactionWithBlock:^{ + // Flush previous data + RLMResults *realmReactionCounts = [MXRealmReactionCount objectsInRealm:self.realm + where:@"eventId = %@", eventId]; + [realm deleteObjects:realmReactionCounts]; + + // Set new one for (MXReactionCount *reactionCount in reactionCounts) { MXRealmReactionCount *realmReactionCount = [self.mapper realmReactionCountFromReactionCount:reactionCount From 87407d0e3f152b5ee1bfa0c130bbeb775595924e Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 24 May 2019 10:00:34 +0200 Subject: [PATCH 13/15] Reactions: Last changes require a clear cache --- MatrixSDK/Data/Store/MXFileStore/MXFileStore.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m index c039b86b0c..241c35d00c 100644 --- a/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m +++ b/MatrixSDK/Data/Store/MXFileStore/MXFileStore.m @@ -25,7 +25,7 @@ #import "MXSDKOptions.h" #import "MXTools.h" -static NSUInteger const kMXFileVersion = 64; +static NSUInteger const kMXFileVersion = 65; static NSString *const kMXFileStoreFolder = @"MXFileStore"; static NSString *const kMXFileStoreMedaDataFile = @"MXFileStore"; From 7e5d3e2c4ef3f1b5996fc9390d2095387d90e1fb Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 24 May 2019 10:07:52 +0200 Subject: [PATCH 14/15] pragma mark! --- MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h | 4 ++++ MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m | 11 +++++++++++ MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h | 3 +++ MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m | 10 +++++++++- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h index 03b5ae3bc7..756b1a7617 100644 --- a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h +++ b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.h @@ -27,12 +27,16 @@ NS_ASSUME_NONNULL_BEGIN aggregationStore:(id)store matrixStore:(id)matrixStore; +#pragma mark - Data access //- (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId; //- (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId; +#pragma mark - Data update listener //- (id)listenToEditsUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; //- (void)removeListener:(id)listener; +#pragma mark - Data update +//- (void)handleOriginalAggregatedDataOfEvent:(MXEvent *)event replaces:(MXEventReplaceChunk*)replaces; - (void)handleReplace:(MXEvent *)event direction:(MXTimelineDirection)direction; - (void)handleRedaction:(MXEvent *)event; diff --git a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m index b03112ad4b..eefc4f5c8d 100644 --- a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m +++ b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m @@ -43,12 +43,23 @@ - (instancetype)initWithMyUser:(NSString*)userId return self; } + +#pragma mark - Data access + //- (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId; //- (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId; + +#pragma mark - Data update listener + //- (id)listenToEditsUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; //- (void)removeListener:(id)listener; + +#pragma mark - Data update + +//- (void)handleOriginalAggregatedDataOfEvent:(MXEvent *)event replaces:(MXEventReplaceChunk*)replaces; + - (void)handleReplace:(MXEvent *)event direction:(MXTimelineDirection)direction { diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h index 6b15da28e7..f06c0e2fbd 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.h @@ -31,12 +31,15 @@ NS_ASSUME_NONNULL_BEGIN aggregationStore:(id)store matrixStore:(id)matrixStore; +#pragma mark - Data access - (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventId inRoom:(NSString*)roomId; - (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId; +#pragma mark - Data update listener - (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block; - (void)removeListener:(id)listener; +#pragma mark - Data update - (void)handleOriginalAggregatedDataOfEvent:(MXEvent *)event annotations:(MXEventAnnotationChunk*)annotations; - (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction; - (void)handleRedaction:(MXEvent *)event; diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m index 15e01d834d..8a1e7d642b 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m @@ -46,6 +46,9 @@ - (instancetype)initWithMyUser:(NSString *)userId aggregationStore:(id *reactions = [self.store reactionCountsOnEvent:eventId]; @@ -65,11 +68,15 @@ - (nullable MXAggregatedReactions *)aggregatedReactionsOnEvent:(NSString*)eventI return aggregatedReactions; } + - (nullable MXReactionCount*)reactionCountForReaction:(NSString*)reaction onEvent:(NSString*)eventId { return [self.store reactionCountForReaction:reaction onEvent:eventId]; } + +#pragma mark - Data update listener + - (id)listenToReactionCountUpdateInRoom:(NSString *)roomId block:(void (^)(NSDictionary * _Nonnull))block { MXReactionCountChangeListener *listener = [MXReactionCountChangeListener new]; @@ -87,6 +94,8 @@ - (void)removeListener:(id)listener } +#pragma mark - Data update + - (void)handleOriginalAggregatedDataOfEvent:(MXEvent *)event annotations:(MXEventAnnotationChunk*)annotations { NSMutableArray *reactions; @@ -113,7 +122,6 @@ - (void)handleOriginalAggregatedDataOfEvent:(MXEvent *)event annotations:(MXEven } } - - (void)handleReaction:(MXEvent *)event direction:(MXTimelineDirection)direction { NSString *parentEventId = event.relatesTo.eventId; From 683f58380708c6385ce8c1233653b4388132c255 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 24 May 2019 11:17:43 +0200 Subject: [PATCH 15/15] Reactions: Fixed Steve's comments --- MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m | 2 +- MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m index eefc4f5c8d..747fc238d4 100644 --- a/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m +++ b/MatrixSDK/Aggregations/MXAggregatedEditsUpdater.m @@ -18,7 +18,7 @@ @interface MXAggregatedEditsUpdater () -@property (nonatomic, weak) NSString *myUserId; +@property (nonatomic) NSString *myUserId; @property (nonatomic, weak) id matrixStore; @property (nonatomic, weak) id store; //@property (nonatomic) NSMutableArray *listeners; diff --git a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m index 8a1e7d642b..597801a1b8 100644 --- a/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m +++ b/MatrixSDK/Aggregations/MXAggregatedReactionsUpdater.m @@ -23,7 +23,7 @@ @interface MXAggregatedReactionsUpdater () -@property (nonatomic, weak) NSString *myUserId; +@property (nonatomic) NSString *myUserId; @property (nonatomic, weak) id matrixStore; @property (nonatomic, weak) id store; @property (nonatomic) NSMutableArray *listeners;