From 7af387251608d8a7de86bdee5e5ca987f52f1bdc Mon Sep 17 00:00:00 2001
From: giomfo <giom@matrix.org>
Date: Sat, 16 Jul 2016 00:40:21 +0200
Subject: [PATCH 1/4] Redacting membership events should immediately reset the
 displayname & avatar of room members.

https://github.com/vector-im/vector-ios/issues/443

- MXEventTimeline: prepare state event redaction.
- MXRoom Notification break: rename "kMXRoomSyncWithLimitedTimelineNotification" with "kMXRoomDidFlushMessagesNotification"
---
 MatrixSDK/Data/MXEventTimeline.m | 152 +++++++++++++++++++++++++++++--
 MatrixSDK/Data/MXRoom.h          |   6 +-
 MatrixSDK/Data/MXRoom.m          |   2 +-
 3 files changed, 147 insertions(+), 13 deletions(-)

diff --git a/MatrixSDK/Data/MXEventTimeline.m b/MatrixSDK/Data/MXEventTimeline.m
index 9232e3b6ce..52896abc4f 100644
--- a/MatrixSDK/Data/MXEventTimeline.m
+++ b/MatrixSDK/Data/MXEventTimeline.m
@@ -19,6 +19,8 @@
 #import "MXSession.h"
 #import "MXMemoryStore.h"
 
+#import "MXEvent+MatrixKit.h"
+
 #import "MXError.h"
 
 NSString *const kMXRoomInviteStateEventIdPrefix = @"invite-";
@@ -47,6 +49,11 @@ @interface MXEventTimeline ()
     // past timelines is managed locally.
     NSString *forwardsPaginationToken;
     BOOL hasReachedHomeServerForwardsPaginationEnd;
+    
+    /**
+     The current pending request.
+     */
+    MXHTTPOperation *httpOperation;
 }
 @end
 
@@ -102,6 +109,13 @@ - (void)initialiseState:(NSArray<MXEvent *> *)stateEvents
 
 - (void)destroy
 {
+    if (httpOperation)
+    {
+        // Cancel the current server request
+        [httpOperation cancel];
+        httpOperation = nil;
+    }
+    
     if (!_isLiveTimeline)
     {
         // Release past timeline events stored in memory
@@ -409,7 +423,7 @@ - (void)handleJoinedRoomSync:(MXRoomSync *)roomSync
     else if (roomSync.timeline.limited)
     {
         // The room has been resync with a limited timeline - Post notification
-        [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomSyncWithLimitedTimelineNotification
+        [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushMessagesNotification
                                                             object:room
                                                           userInfo:nil];
     }
@@ -537,7 +551,9 @@ - (void)addEvent:(MXEvent*)event direction:(MXTimelineDirection)direction fromSt
 #pragma mark - Specific events Handling
 - (void)handleRedaction:(MXEvent*)redactionEvent
 {
-    // Check whether the redacted event has been already processed
+    NSLog(@"[MXEventTimeline] handle an event redaction");
+    
+    // Check whether the redacted event is stored in room messages
     MXEvent *redactedEvent = [store eventWithEventId:redactionEvent.redacts inRoom:_state.roomId];
     if (redactedEvent)
     {
@@ -545,15 +561,133 @@ - (void)handleRedaction:(MXEvent*)redactionEvent
         redactedEvent = [redactedEvent prune];
         redactedEvent.redactedBecause = redactionEvent.JSONDictionary;
 
-        if (redactedEvent.isState) {
-            // FIXME: The room state must be refreshed here since this redacted event.
-        }
-
-        // Store the event
+        // Store the updated event
         [store replaceEvent:redactedEvent inRoom:_state.roomId];
     }
+    
+    // Check whether the current room state depends on this redacted event.
+    if (!redactedEvent || redactedEvent.isState)
+    {
+        // FIXME: Is @autoreleasepool block required or not here?
+        NSArray *stateEvents = _state.stateEvents;
+        
+        for (MXEvent *stateEvent in stateEvents)
+        {
+            if ([stateEvent.eventId isEqualToString:redactionEvent.redacts])
+            {
+                NSLog(@"[MXEventTimeline] the current room state has been modified by the event redaction.");
+                
+                // Redact the stored event
+                redactedEvent = [stateEvent prune];
+                redactedEvent.redactedBecause = redactionEvent.JSONDictionary;
+                
+                // Reset the room state. //FIXME: is it possible to handle only the redacted event?
+                _state = [[MXRoomState alloc] initWithRoomId:room.roomId andMatrixSession:room.mxSession andDirection:YES];
+                [self initialiseState:stateEvents];
+                
+                // Update store with new room state when all state event have been processed
+                if ([store respondsToSelector:@selector(storeStateForRoom:stateEvents:)])
+                {
+                    [store storeStateForRoom:_state.roomId stateEvents:_state.stateEvents];
+                }
+                
+                // Reset the current pagination
+                // FIXME: Shall we let the kMXRoomStateHasBeenRedactedNotification listener trigger this reset? (like [MXKRoomDataSource reload] do)
+                [self resetPagination];
+                
+                // Notify that room history has been flushed
+                [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushMessagesNotification
+                                                                    object:room
+                                                                  userInfo:nil];
+                return;
+            }
+        }
+    }
+    
+    // Re-sync the room in case of redacted state event from the past.
+    // Indeed, redacted information shouldn't spontaneously appear when you backpaginate...
+    if (!redactedEvent)
+    {
+        // Use a /context request to check whether the redacted event is a state event or not.
+        httpOperation = [room.mxSession.matrixRestClient contextOfEvent:redactionEvent.redacts inRoom:room.roomId limit:1 success:^(MXEventContext *eventContext) {
+            
+            if (!httpOperation)
+            {
+                return;
+            }
+            httpOperation = nil;
+            
+            if (eventContext.event.isState)
+            {
+                NSLog(@"[MXEventTimeline] the redacted event is a state event from the past");
+                [self forceRoomServerSync];
+            }
+            
+        } failure:^(NSError *error) {
+            
+            if (!httpOperation)
+            {
+                return;
+            }
+            httpOperation = nil;
+            
+            NSLog(@"[MXEventTimeline] handleRedaction: failed to retrieved the redacted event");
+            [self forceRoomServerSync];
+        }];
+    }
+    else if (redactedEvent.isState)
+    {
+        NSLog(@"[MXEventTimeline] the redacted event is a former state event");
+        [self forceRoomServerSync];
+    }
 }
 
+- (void)forceRoomServerSync
+{
+    // Reset the storage of this room. Re-sync it from the server
+    NSLog(@"[MXEventTimeline] re-sync room (%@) from the server.", room.roomId);
+    [store deleteRoom:room.roomId];
+    
+    // Make an /initialSync request to get data
+    // Use a 0 messages limit for now because:
+    //    - /initialSync is marked as obsolete in the spec
+    //    - MXEventTimeline does not have methods to handle /initialSync responses
+    // So, avoid to write temparary code and let the user uses [MXEventTimeline paginate]
+    // to get room messages.
+    httpOperation = [room.mxSession.matrixRestClient initialSyncOfRoom:room.roomId withLimit:0 success:^(MXRoomInitialSync *roomInitialSync) {
+        
+        if (!httpOperation)
+        {
+            return;
+        }
+        httpOperation = nil;
+        
+        _state = [[MXRoomState alloc] initWithRoomId:room.roomId andMatrixSession:room.mxSession andDirection:YES];
+        [self initialiseState:roomInitialSync.state];
+        
+        // Update store with new room state when all state event have been processed
+        if ([store respondsToSelector:@selector(storeStateForRoom:stateEvents:)])
+        {
+            [store storeStateForRoom:_state.roomId stateEvents:_state.stateEvents];
+        }
+        
+        [self resetPagination];
+        
+        // Notify that room history has been flushed
+        [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushMessagesNotification
+                                                            object:room
+                                                          userInfo:nil];
+        
+    } failure:^(NSError *error) {
+        
+        NSLog(@"[MXEventTimeline] forceRoomServerSync failed.");
+        
+        // FIXME: Reload entirely the app?
+//        [[NSNotificationCenter defaultCenter] postNotificationName:kMXSessionIgnoredUsersDidChangeNotification
+//                                                            object:room.mxSession
+//                                                          userInfo:nil];
+    }];
+}
 
 #pragma mark - State events handling
 - (void)cloneState:(MXTimelineDirection)direction
@@ -584,8 +718,8 @@ - (void)handleStateEvent:(MXEvent*)event direction:(MXTimelineDirection)directio
         // Forwards events update the current state of the room
         [_state handleStateEvent:event];
 
-        // Special handling for presence
-        if (_isLiveTimeline && MXEventTypeRoomMember == event.eventType)
+        // Special handling for presence (CAUTION: ignore redacted state event here)
+        if (_isLiveTimeline && MXEventTypeRoomMember == event.eventType && !event.isRedactedEvent)
         {
             // Update MXUser data
             MXUser *user = [room.mxSession getOrCreateUser:event.sender];
diff --git a/MatrixSDK/Data/MXRoom.h b/MatrixSDK/Data/MXRoom.h
index d9525a88e4..fb5fb0810b 100644
--- a/MatrixSDK/Data/MXRoom.h
+++ b/MatrixSDK/Data/MXRoom.h
@@ -39,13 +39,13 @@
 FOUNDATION_EXPORT NSString *const kMXRoomInitialSyncNotification;
 
 /**
- Posted when a limited timeline is observed for an existing room during server sync.
- All the existing messages have been removed from the room storage. Only the messages received during this sync are available.
+ Posted when the messages of an existing room has been flushed during server sync.
+ This flush may be due to a limited timeline in the room sync, or the redaction of a state event.
  The token where to start back pagination has been updated.
  
  The notification object is the concerned room (MXRoom instance).
  */
-FOUNDATION_EXPORT NSString *const kMXRoomSyncWithLimitedTimelineNotification;
+FOUNDATION_EXPORT NSString *const kMXRoomDidFlushMessagesNotification;
 
 /**
  Posted when the number of unread notifications ('notificationCount' and 'highlightCount' properties) are updated.
diff --git a/MatrixSDK/Data/MXRoom.m b/MatrixSDK/Data/MXRoom.m
index 6d71954f78..53600b66aa 100644
--- a/MatrixSDK/Data/MXRoom.m
+++ b/MatrixSDK/Data/MXRoom.m
@@ -21,7 +21,7 @@
 
 #import "MXError.h"
 
-NSString *const kMXRoomSyncWithLimitedTimelineNotification = @"kMXRoomSyncWithLimitedTimelineNotification";
+NSString *const kMXRoomDidFlushMessagesNotification = @"kMXRoomDidFlushMessagesNotification";
 NSString *const kMXRoomInitialSyncNotification = @"kMXRoomInitialSyncNotification";
 NSString *const kMXRoomDidUpdateUnreadNotification = @"kMXRoomDidUpdateUnreadNotification";
 

From ae312a3dca705f1e8a4eda42203fb8da5b80b067 Mon Sep 17 00:00:00 2001
From: giomfo <giom@matrix.org>
Date: Mon, 18 Jul 2016 01:03:29 +0200
Subject: [PATCH 2/4] Redacting membership events should immediately reset the
 displayname & avatar of room members.

vector-im/vector-ios#443

- Fix: The redacted state event was ignored in room state handling
- Add FIXME related to the blocker issue on server side SYN-724 ("prev_content is totally pruned by the server on redacted membership event").
---
 MatrixSDK/Data/MXEventTimeline.m |  8 ++++++--
 MatrixSDK/Data/MXRoomState.m     | 21 +++++++++++++--------
 2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/MatrixSDK/Data/MXEventTimeline.m b/MatrixSDK/Data/MXEventTimeline.m
index 52896abc4f..f155626ba6 100644
--- a/MatrixSDK/Data/MXEventTimeline.m
+++ b/MatrixSDK/Data/MXEventTimeline.m
@@ -569,10 +569,12 @@ - (void)handleRedaction:(MXEvent*)redactionEvent
     if (!redactedEvent || redactedEvent.isState)
     {
         // FIXME: Is @autoreleasepool block required or not here?
-        NSArray *stateEvents = _state.stateEvents;
+        NSMutableArray *stateEvents = [NSMutableArray arrayWithArray:_state.stateEvents];
         
-        for (MXEvent *stateEvent in stateEvents)
+        for (NSInteger index = 0; index < stateEvents.count; index++)
         {
+            MXEvent *stateEvent = stateEvents[index];
+            
             if ([stateEvent.eventId isEqualToString:redactionEvent.redacts])
             {
                 NSLog(@"[MXEventTimeline] the current room state has been modified by the event redaction.");
@@ -581,6 +583,8 @@ - (void)handleRedaction:(MXEvent*)redactionEvent
                 redactedEvent = [stateEvent prune];
                 redactedEvent.redactedBecause = redactionEvent.JSONDictionary;
                 
+                [stateEvents replaceObjectAtIndex:index withObject:redactedEvent];
+                
                 // Reset the room state. //FIXME: is it possible to handle only the redacted event?
                 _state = [[MXRoomState alloc] initWithRoomId:room.roomId andMatrixSession:room.mxSession andDirection:YES];
                 [self initialiseState:stateEvents];
diff --git a/MatrixSDK/Data/MXRoomState.m b/MatrixSDK/Data/MXRoomState.m
index 183484f906..0118c8f4ff 100644
--- a/MatrixSDK/Data/MXRoomState.m
+++ b/MatrixSDK/Data/MXRoomState.m
@@ -450,7 +450,8 @@ - (void)handleStateEvent:(MXEvent*)event
                     membersWithThirdPartyInviteTokenCache[roomMember.thirdPartyInviteToken] = roomMember;
                 }
             }
-            else
+            else// FIXME: In case of redacted membership event (prev_content is missing see SYN-724),
+                // the room member is then nil: the client considers by mistake the user is no more part of the room...
             {
                 // The user is no more part of the room. Remove him.
                 // This case happens during back pagination: we remove here users when they are not in the room yet.
@@ -550,13 +551,17 @@ - (NSString*)memberName:(NSString*)userId
         
         if (!member)
         {
-            // The user may not have joined the room yet. So try to resolve display name from presence data
-            // Note: This data may not be available
-            MXUser* user = [mxSession userWithUserId:userId];
-            if (user && user.displayname.length)
-            {
-                displayName = user.displayname;
-            }
+            // FIXME: In case of redacted membership event (prev_content is missing see SYN-724),
+            // the client considers by mistake that some users are no more part of the room.
+            // PATCH: we comment the following, else some redacted information re-appear spontaneously.
+            
+//            // The user may not have joined the room yet. So try to resolve display name from presence data
+//            // Note: This data may not be available
+//            MXUser* user = [mxSession userWithUserId:userId];
+//            if (user && user.displayname.length)
+//            {
+//                displayName = user.displayname;
+//            }
         }
         else if (member.displayname.length)
         {

From a8b97fb529af202c2a5b2c6117fa7b7d4ff2fac5 Mon Sep 17 00:00:00 2001
From: giomfo <giom@matrix.org>
Date: Thu, 11 Aug 2016 17:52:43 +0200
Subject: [PATCH 3/4] Redacting membership events should immediately reset the
 displayname & avatar of room members.

vector-im/vector-ios#443

- remove FIXME (the blocker issue SYN-724 has been fixed on server side).
---
 MatrixSDK/Data/MXEventTimeline.m | 13 ++++++-------
 MatrixSDK/Data/MXRoomState.m     | 21 ++++++++-------------
 MatrixSDK/MXSession.h            |  8 ++++++++
 MatrixSDK/MXSession.m            |  1 +
 4 files changed, 23 insertions(+), 20 deletions(-)

diff --git a/MatrixSDK/Data/MXEventTimeline.m b/MatrixSDK/Data/MXEventTimeline.m
index f155626ba6..d51e7f4953 100644
--- a/MatrixSDK/Data/MXEventTimeline.m
+++ b/MatrixSDK/Data/MXEventTimeline.m
@@ -568,7 +568,6 @@ - (void)handleRedaction:(MXEvent*)redactionEvent
     // Check whether the current room state depends on this redacted event.
     if (!redactedEvent || redactedEvent.isState)
     {
-        // FIXME: Is @autoreleasepool block required or not here?
         NSMutableArray *stateEvents = [NSMutableArray arrayWithArray:_state.stateEvents];
         
         for (NSInteger index = 0; index < stateEvents.count; index++)
@@ -585,7 +584,7 @@ - (void)handleRedaction:(MXEvent*)redactionEvent
                 
                 [stateEvents replaceObjectAtIndex:index withObject:redactedEvent];
                 
-                // Reset the room state. //FIXME: is it possible to handle only the redacted event?
+                // Reset the room state.
                 _state = [[MXRoomState alloc] initWithRoomId:room.roomId andMatrixSession:room.mxSession andDirection:YES];
                 [self initialiseState:stateEvents];
                 
@@ -596,7 +595,7 @@ - (void)handleRedaction:(MXEvent*)redactionEvent
                 }
                 
                 // Reset the current pagination
-                // FIXME: Shall we let the kMXRoomStateHasBeenRedactedNotification listener trigger this reset? (like [MXKRoomDataSource reload] do)
+                // FIXME: Shall we let the kMXRoomDidFlushMessagesNotification listener trigger this reset? (like [MXKRoomDataSource reload] do)
                 [self resetPagination];
                 
                 // Notify that room history has been flushed
@@ -686,10 +685,10 @@ - (void)forceRoomServerSync
         
         NSLog(@"[MXEventTimeline] forceRoomServerSync failed.");
         
-        // FIXME: Reload entirely the app?
-//        [[NSNotificationCenter defaultCenter] postNotificationName:kMXSessionIgnoredUsersDidChangeNotification
-//                                                            object:room.mxSession
-//                                                          userInfo:nil];
+        // Reload entirely the app
+        [[NSNotificationCenter defaultCenter] postNotificationName:kMXSessionDidCorruptDataNotification
+                                                            object:room.mxSession
+                                                          userInfo:nil];
     }];
 }
 
diff --git a/MatrixSDK/Data/MXRoomState.m b/MatrixSDK/Data/MXRoomState.m
index 8174f9c1a4..41722b21a0 100644
--- a/MatrixSDK/Data/MXRoomState.m
+++ b/MatrixSDK/Data/MXRoomState.m
@@ -462,8 +462,7 @@ - (void)handleStateEvent:(MXEvent*)event
                     membersWithThirdPartyInviteTokenCache[roomMember.thirdPartyInviteToken] = roomMember;
                 }
             }
-            else// FIXME: In case of redacted membership event (prev_content is missing see SYN-724),
-                // the room member is then nil: the client considers by mistake the user is no more part of the room...
+            else
             {
                 // The user is no more part of the room. Remove him.
                 // This case happens during back pagination: we remove here users when they are not in the room yet.
@@ -568,17 +567,13 @@ - (NSString*)memberName:(NSString*)userId
         
         if (!member)
         {
-            // FIXME: In case of redacted membership event (prev_content is missing see SYN-724),
-            // the client considers by mistake that some users are no more part of the room.
-            // PATCH: we comment the following, else some redacted information re-appear spontaneously.
-            
-//            // The user may not have joined the room yet. So try to resolve display name from presence data
-//            // Note: This data may not be available
-//            MXUser* user = [mxSession userWithUserId:userId];
-//            if (user && user.displayname.length)
-//            {
-//                displayName = user.displayname;
-//            }
+            // The user may not have joined the room yet. So try to resolve display name from presence data
+            // Note: This data may not be available
+            MXUser* user = [mxSession userWithUserId:userId];
+            if (user && user.displayname.length)
+            {
+                displayName = user.displayname;
+            }
         }
         else if (member.displayname.length)
         {
diff --git a/MatrixSDK/MXSession.h b/MatrixSDK/MXSession.h
index 01c9af6353..0c2641c2e9 100644
--- a/MatrixSDK/MXSession.h
+++ b/MatrixSDK/MXSession.h
@@ -161,9 +161,17 @@ FOUNDATION_EXPORT NSString *const kMXSessionNotificationEventKey;
 
 /**
  Posted when MXSession has detected a change in the `ignoredUsers` property.
+ 
+ The notification object is the concerned session (MXSession instance).
  */
 FOUNDATION_EXPORT NSString *const kMXSessionIgnoredUsersDidChangeNotification;
 
+/**
+ Posted when MXSession data have been corrupted. The listener must reload the session data with a full server sync.
+ 
+ The notification object is the concerned session (MXSession instance).
+ */
+FOUNDATION_EXPORT NSString *const kMXSessionDidCorruptDataNotification;
 
 #pragma mark - Other constants
 /**
diff --git a/MatrixSDK/MXSession.m b/MatrixSDK/MXSession.m
index 1f8fa8816c..6f83879992 100644
--- a/MatrixSDK/MXSession.m
+++ b/MatrixSDK/MXSession.m
@@ -40,6 +40,7 @@
 NSString *const kMXSessionNotificationRoomIdKey = @"roomId";
 NSString *const kMXSessionNotificationEventKey = @"event";
 NSString *const kMXSessionIgnoredUsersDidChangeNotification = @"kMXSessionIgnoredUsersDidChangeNotification";
+NSString *const kMXSessionDidCorruptDataNotification = @"kMXSessionDidCorruptDataNotification";
 NSString *const kMXSessionNoRoomTag = @"m.recent";  // Use the same value as matrix-react-sdk
 
 /**

From e1158a4ea56c2672f7a116231fb3475a43034de0 Mon Sep 17 00:00:00 2001
From: giomfo <giom@matrix.org>
Date: Fri, 12 Aug 2016 16:47:57 +0200
Subject: [PATCH 4/4] Redacting membership events should immediately reset the
 displayname & avatar of room members.

vector-im/vector-ios#443

Fix Manu's comments (https://github.com/matrix-org/matrix-ios-sdk/pull/118).
---
 MatrixSDK/Data/MXEventTimeline.m | 13 +++++--------
 MatrixSDK/Data/MXRoom.h          |  2 +-
 MatrixSDK/Data/MXRoom.m          |  2 +-
 MatrixSDK/JSONModels/MXEvent.h   | 12 +++++++++++-
 MatrixSDK/JSONModels/MXEvent.m   | 19 +++++++++++++++++++
 5 files changed, 37 insertions(+), 11 deletions(-)

diff --git a/MatrixSDK/Data/MXEventTimeline.m b/MatrixSDK/Data/MXEventTimeline.m
index d51e7f4953..c711763391 100644
--- a/MatrixSDK/Data/MXEventTimeline.m
+++ b/MatrixSDK/Data/MXEventTimeline.m
@@ -19,8 +19,6 @@
 #import "MXSession.h"
 #import "MXMemoryStore.h"
 
-#import "MXEvent+MatrixKit.h"
-
 #import "MXError.h"
 
 NSString *const kMXRoomInviteStateEventIdPrefix = @"invite-";
@@ -423,7 +421,7 @@ - (void)handleJoinedRoomSync:(MXRoomSync *)roomSync
     else if (roomSync.timeline.limited)
     {
         // The room has been resync with a limited timeline - Post notification
-        [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushMessagesNotification
+        [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushDataNotification
                                                             object:room
                                                           userInfo:nil];
     }
@@ -595,11 +593,10 @@ - (void)handleRedaction:(MXEvent*)redactionEvent
                 }
                 
                 // Reset the current pagination
-                // FIXME: Shall we let the kMXRoomDidFlushMessagesNotification listener trigger this reset? (like [MXKRoomDataSource reload] do)
                 [self resetPagination];
                 
                 // Notify that room history has been flushed
-                [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushMessagesNotification
+                [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushDataNotification
                                                                     object:room
                                                                   userInfo:nil];
                 return;
@@ -677,7 +674,7 @@ - (void)forceRoomServerSync
         [self resetPagination];
         
         // Notify that room history has been flushed
-        [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushMessagesNotification
+        [[NSNotificationCenter defaultCenter] postNotificationName:kMXRoomDidFlushDataNotification
                                                             object:room
                                                           userInfo:nil];
         
@@ -721,10 +718,10 @@ - (void)handleStateEvent:(MXEvent*)event direction:(MXTimelineDirection)directio
         // Forwards events update the current state of the room
         [_state handleStateEvent:event];
 
-        // Special handling for presence (CAUTION: ignore redacted state event here)
+        // Special handling for presence: update MXUser data in case of membership event.
+        // CAUTION: ignore here redacted state event, the redaction concerns only the context of the event room.
         if (_isLiveTimeline && MXEventTypeRoomMember == event.eventType && !event.isRedactedEvent)
         {
-            // Update MXUser data
             MXUser *user = [room.mxSession getOrCreateUser:event.sender];
 
             MXRoomMember *roomMember = [_state memberWithUserId:event.sender];
diff --git a/MatrixSDK/Data/MXRoom.h b/MatrixSDK/Data/MXRoom.h
index e43766f69e..46e0034c14 100644
--- a/MatrixSDK/Data/MXRoom.h
+++ b/MatrixSDK/Data/MXRoom.h
@@ -45,7 +45,7 @@ FOUNDATION_EXPORT NSString *const kMXRoomInitialSyncNotification;
  
  The notification object is the concerned room (MXRoom instance).
  */
-FOUNDATION_EXPORT NSString *const kMXRoomDidFlushMessagesNotification;
+FOUNDATION_EXPORT NSString *const kMXRoomDidFlushDataNotification;
 
 /**
  Posted when the number of unread notifications ('notificationCount' and 'highlightCount' properties) are updated.
diff --git a/MatrixSDK/Data/MXRoom.m b/MatrixSDK/Data/MXRoom.m
index 26c968b542..c955b47f9c 100644
--- a/MatrixSDK/Data/MXRoom.m
+++ b/MatrixSDK/Data/MXRoom.m
@@ -21,7 +21,7 @@
 
 #import "MXError.h"
 
-NSString *const kMXRoomDidFlushMessagesNotification = @"kMXRoomDidFlushMessagesNotification";
+NSString *const kMXRoomDidFlushDataNotification = @"kMXRoomDidFlushDataNotification";
 NSString *const kMXRoomInitialSyncNotification = @"kMXRoomInitialSyncNotification";
 NSString *const kMXRoomDidUpdateUnreadNotification = @"kMXRoomDidUpdateUnreadNotification";
 
diff --git a/MatrixSDK/JSONModels/MXEvent.h b/MatrixSDK/JSONModels/MXEvent.h
index fa286da2fa..0ce6a61d4c 100644
--- a/MatrixSDK/JSONModels/MXEvent.h
+++ b/MatrixSDK/JSONModels/MXEvent.h
@@ -216,10 +216,20 @@ FOUNDATION_EXPORT uint64_t const kMXUndefinedTimestamp;
 - (MXEventType)eventType;
 
 /**
- Indicates if the event hosts state data
+ Indicates if the event hosts state data.
  */
 - (BOOL)isState;
 
+/**
+ Indicates if the event has been redacted.
+ */
+- (BOOL)isRedactedEvent;
+
+/**
+ Return YES if the event is an emote event
+ */
+- (BOOL)isEmote;
+
 /**
  Returns the event IDs for which a read receipt is defined in this event.
  
diff --git a/MatrixSDK/JSONModels/MXEvent.m b/MatrixSDK/JSONModels/MXEvent.m
index 37e03559d9..e08a9c29c1 100644
--- a/MatrixSDK/JSONModels/MXEvent.m
+++ b/MatrixSDK/JSONModels/MXEvent.m
@@ -225,6 +225,25 @@ - (BOOL)isState
     return (nil != self.stateKey);
 }
 
+- (BOOL)isRedactedEvent
+{
+    // The event is redacted if its redactedBecause is filed (with a redaction event id)
+    return (self.redactedBecause != nil);
+}
+
+- (BOOL)isEmote
+{
+    if (self.eventType == MXEventTypeRoomMessage)
+    {
+        NSString *msgtype = self.content[@"msgtype"];
+        if ([msgtype isEqualToString:kMXMessageTypeEmote])
+        {
+            return YES;
+        }
+    }
+    return NO;
+}
+
 - (NSArray *)readReceiptEventIds
 {
     NSMutableArray* list = nil;