diff --git a/CHANGES.md b/CHANGES.md
index 093d2c0b86..f7b8847bbd 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -2,7 +2,7 @@ Changes in RiotX 0.8.0 (2019-XX-XX)
 ===================================================
 
 Features ✨:
- -
+ - Ignore/UnIgnore users, and display list of ignored users (#542, #617)
 
 Improvements 🙌:
  - Search reaction by name or keyword in emoji picker
diff --git a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
index f19777b6f5..1572851d3a 100644
--- a/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
+++ b/matrix-sdk-android-rx/src/main/java/im/vector/matrix/rx/RxSession.kt
@@ -54,6 +54,10 @@ class RxSession(private val session: Session) {
         return session.liveUsers().asObservable()
     }
 
+    fun liveIgnoredUsers(): Observable<List<User>> {
+        return session.liveIgnoredUsers().asObservable()
+    }
+
     fun livePagedUsers(filter: String? = null): Observable<PagedList<User>> {
         return session.livePagedUsers(filter).asObservable()
     }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/cache/CacheService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/cache/CacheService.kt
index f1e4ca6c7b..a84e5af48c 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/cache/CacheService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/cache/CacheService.kt
@@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.cache
 import im.vector.matrix.android.api.MatrixCallback
 
 /**
- * This interface defines a method to sign out. It's implemented at the session level.
+ * This interface defines a method to clear the cache. It's implemented at the session level.
  */
 interface CacheService {
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt
index d3de777e34..2a93a876f6 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/user/UserService.kt
@@ -64,4 +64,19 @@ interface UserService {
      * @return a Livedata of users
      */
     fun livePagedUsers(filter: String? = null): LiveData<PagedList<User>>
+
+    /**
+     * Get list of ignored users
+     */
+    fun liveIgnoredUsers(): LiveData<List<User>>
+
+    /**
+     * Ignore users
+     */
+    fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
+
+    /**
+     * Un-ignore some users
+     */
+    fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt
index bfb9a59956..f83166512e 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/util/Types.kt
@@ -21,4 +21,6 @@ import java.lang.reflect.ParameterizedType
 
 typealias JsonDict = Map<String, @JvmSuppressWildcards Any>
 
+val emptyJsonDict = emptyMap<String, Any>()
+
 internal val JSON_DICT_PARAMETERIZED_TYPE: ParameterizedType = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/IgnoredUserEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/IgnoredUserEntity.kt
new file mode 100644
index 0000000000..bd31046f82
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/IgnoredUserEntity.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.database.model
+
+import io.realm.RealmObject
+
+internal open class IgnoredUserEntity(var userId: String = "") : RealmObject() {
+
+    companion object
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt
index 21b2fdce5a..76b355b064 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/SessionRealmModule.kt
@@ -35,6 +35,7 @@ import io.realm.annotations.RealmModule
                  RoomTagEntity::class,
                  SyncEntity::class,
                  UserEntity::class,
+                 IgnoredUserEntity::class,
                  EventAnnotationsSummaryEntity::class,
                  ReactionAggregatedSummaryEntity::class,
                  EditAggregatedSummaryEntity::class,
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt
index d8db462f7c..96cdf29226 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MoshiProvider.kt
@@ -20,10 +20,11 @@ import com.squareup.moshi.Moshi
 import im.vector.matrix.android.api.session.room.model.message.*
 import im.vector.matrix.android.internal.network.parsing.RuntimeJsonAdapterFactory
 import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter
-import im.vector.matrix.android.internal.session.sync.model.UserAccountData
-import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
-import im.vector.matrix.android.internal.session.sync.model.UserAccountDataFallback
-import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataDirectMessages
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataFallback
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataIgnoredUsers
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataPushRules
 
 object MoshiProvider {
 
@@ -31,6 +32,7 @@ object MoshiProvider {
             .add(UriMoshiAdapter())
             .add(RuntimeJsonAdapterFactory.of(UserAccountData::class.java, "type", UserAccountDataFallback::class.java)
                     .registerSubtype(UserAccountDataDirectMessages::class.java, UserAccountData.TYPE_DIRECT_MESSAGES)
+                    .registerSubtype(UserAccountDataIgnoredUsers::class.java, UserAccountData.TYPE_IGNORED_USER_LIST)
                     .registerSubtype(UserAccountDataPushRules::class.java, UserAccountData.TYPE_PUSH_RULES)
             )
             .add(RuntimeJsonAdapterFactory.of(MessageContent::class.java, "msgtype", MessageDefaultContent::class.java)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
index 56b96b428d..56bc005805 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/UserAccountDataSyncHandler.kt
@@ -27,10 +27,9 @@ import im.vector.matrix.android.internal.di.UserId
 import im.vector.matrix.android.internal.session.pushers.SavePushRulesTask
 import im.vector.matrix.android.internal.session.room.membership.RoomMembers
 import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
-import im.vector.matrix.android.internal.session.sync.model.UserAccountDataDirectMessages
-import im.vector.matrix.android.internal.session.sync.model.UserAccountDataPushRules
-import im.vector.matrix.android.internal.session.sync.model.UserAccountDataSync
+import im.vector.matrix.android.internal.session.sync.model.accountdata.*
 import im.vector.matrix.android.internal.session.user.accountdata.DirectChatsHelper
+import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
 import im.vector.matrix.android.internal.session.user.accountdata.UpdateUserAccountDataTask
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.internal.task.configureWith
@@ -44,6 +43,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
                                                               private val directChatsHelper: DirectChatsHelper,
                                                               private val updateUserAccountDataTask: UpdateUserAccountDataTask,
                                                               private val savePushRulesTask: SavePushRulesTask,
+                                                              private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
                                                               private val taskExecutor: TaskExecutor) {
 
     suspend fun handle(accountData: UserAccountDataSync?, invites: Map<String, InvitedRoomSync>?) {
@@ -51,9 +51,18 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
             when (it) {
                 is UserAccountDataDirectMessages -> handleDirectChatRooms(it)
                 is UserAccountDataPushRules      -> handlePushRules(it)
-                else                             -> return@forEach
+                is UserAccountDataIgnoredUsers   -> handleIgnoredUsers(it)
+                is UserAccountDataFallback       -> Timber.d("Receive account data of unhandled type ${it.type}")
+                else                             -> error("Missing code here!")
             }
         }
+
+        // TODO Store all account data, app can be interested of it
+        // accountData?.list?.forEach {
+        //     it.toString()
+        //     MoshiProvider.providesMoshi()
+        // }
+
         monarchy.doWithRealm { realm ->
             synchronizeWithServerIfNeeded(realm, invites)
         }
@@ -114,4 +123,11 @@ internal class UserAccountDataSyncHandler @Inject constructor(private val monarc
             updateUserAccountDataTask.configureWith(updateUserAccountParams).executeBy(taskExecutor)
         }
     }
+
+    private fun handleIgnoredUsers(userAccountDataIgnoredUsers: UserAccountDataIgnoredUsers) {
+        saveIgnoredUsersTask
+                .configureWith(SaveIgnoredUsersTask.Params(userAccountDataIgnoredUsers.content.ignoredUsers.keys.toList()))
+                .executeBy(taskExecutor)
+        // TODO If not initial sync, we should execute a init sync
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/SyncResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/SyncResponse.kt
index d084dcdadd..9e5cc2cfa4 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/SyncResponse.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/SyncResponse.kt
@@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.sync.model
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataSync
 
 // SyncResponse represents the request response for server sync v2.
 @JsonClass(generateAdapter = true)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/IgnoredUsersContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/IgnoredUsersContent.kt
new file mode 100644
index 0000000000..ea591d79b0
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/IgnoredUsersContent.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.session.sync.model.accountdata
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+import im.vector.matrix.android.api.util.JsonDict
+import im.vector.matrix.android.api.util.emptyJsonDict
+
+@JsonClass(generateAdapter = true)
+internal data class IgnoredUsersContent(
+        /**
+         * Required. The map of users to ignore. UserId -> empty object for future enhancement
+         */
+        @Json(name = "ignored_users") val ignoredUsers: Map<String, JsonDict>
+) {
+
+    companion object {
+        fun createWithUserIds(userIds: List<String>): IgnoredUsersContent {
+            return IgnoredUsersContent(
+                    ignoredUsers = userIds.associateWith { emptyJsonDict }
+            )
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt
similarity index 81%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountData.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt
index 2173d2f4df..55dbad6099 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountData.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountData.kt
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
-package im.vector.matrix.android.internal.session.sync.model
+package im.vector.matrix.android.internal.session.sync.model.accountdata
 
-internal interface UserAccountData {
+import com.squareup.moshi.Json
+
+internal abstract class UserAccountData {
+
+    @Json(name = "type") abstract val type: String
 
     companion object {
         const val TYPE_IGNORED_USER_LIST = "m.ignored_user_list"
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataDirectMessages.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataDirectMessages.kt
similarity index 82%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataDirectMessages.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataDirectMessages.kt
index 825a16cb1e..e5c6135bd1 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataDirectMessages.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataDirectMessages.kt
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package im.vector.matrix.android.internal.session.sync.model
+package im.vector.matrix.android.internal.session.sync.model.accountdata
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 internal data class UserAccountDataDirectMessages(
+        @Json(name = "type") override val type: String = TYPE_DIRECT_MESSAGES,
         @Json(name = "content") val content: Map<String, List<String>>
-) : UserAccountData
+) : UserAccountData()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataFallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataFallback.kt
similarity index 84%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataFallback.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataFallback.kt
index 70d28c084f..a8b8235d37 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataFallback.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataFallback.kt
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-package im.vector.matrix.android.internal.session.sync.model
+package im.vector.matrix.android.internal.session.sync.model.accountdata
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 
 @JsonClass(generateAdapter = true)
 internal data class UserAccountDataFallback(
+        @Json(name = "type") override val type: String,
         @Json(name = "content") val content: Map<String, Any>
-) : UserAccountData
+) : UserAccountData()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataIgnoredUsers.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataIgnoredUsers.kt
new file mode 100644
index 0000000000..63a7604305
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataIgnoredUsers.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.session.sync.model.accountdata
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+internal data class UserAccountDataIgnoredUsers(
+        @Json(name = "type") override val type: String = TYPE_IGNORED_USER_LIST,
+        @Json(name = "content") val content: IgnoredUsersContent
+) : UserAccountData()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataPushRules.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataPushRules.kt
similarity index 83%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataPushRules.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataPushRules.kt
index 7f357c876b..0d549d1667 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataPushRules.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataPushRules.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package im.vector.matrix.android.internal.session.sync.model
+package im.vector.matrix.android.internal.session.sync.model.accountdata
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
@@ -22,5 +22,6 @@ import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
 
 @JsonClass(generateAdapter = true)
 internal data class UserAccountDataPushRules(
+        @Json(name = "type") override val type: String = TYPE_PUSH_RULES,
         @Json(name = "content") val content: GetPushRulesResponse
-) : UserAccountData
+) : UserAccountData()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataSync.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataSync.kt
similarity index 91%
rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataSync.kt
rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataSync.kt
index 4b9e9d652d..c7f8bfa4c2 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/UserAccountDataSync.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/model/accountdata/UserAccountDataSync.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package im.vector.matrix.android.internal.session.sync.model
+package im.vector.matrix.android.internal.session.sync.model.accountdata
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt
index be330bfc36..d314c8d108 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/DefaultUserService.kt
@@ -29,9 +29,12 @@ import im.vector.matrix.android.api.util.Cancelable
 import im.vector.matrix.android.api.util.Optional
 import im.vector.matrix.android.api.util.toOptional
 import im.vector.matrix.android.internal.database.mapper.asDomain
+import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
+import im.vector.matrix.android.internal.database.model.IgnoredUserEntityFields
 import im.vector.matrix.android.internal.database.model.UserEntity
 import im.vector.matrix.android.internal.database.model.UserEntityFields
 import im.vector.matrix.android.internal.database.query.where
+import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
 import im.vector.matrix.android.internal.session.user.model.SearchUserTask
 import im.vector.matrix.android.internal.task.TaskExecutor
 import im.vector.matrix.android.internal.task.configureWith
@@ -40,8 +43,8 @@ import javax.inject.Inject
 
 internal class DefaultUserService @Inject constructor(private val monarchy: Monarchy,
                                                       private val searchUserTask: SearchUserTask,
+                                                      private val updateIgnoredUserIdsTask: UpdateIgnoredUserIdsTask,
                                                       private val taskExecutor: TaskExecutor) : UserService {
-
     private val realmDataSourceFactory: Monarchy.RealmDataSourceFactory<UserEntity> by lazy {
         monarchy.createDataSourceFactory { realm ->
             realm.where(UserEntity::class.java)
@@ -62,7 +65,7 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
 
     override fun getUser(userId: String): User? {
         val userEntity = monarchy.fetchCopied { UserEntity.where(it, userId).findFirst() }
-                         ?: return null
+                ?: return null
 
         return userEntity.asDomain()
     }
@@ -117,4 +120,33 @@ internal class DefaultUserService @Inject constructor(private val monarchy: Mona
                 }
                 .executeBy(taskExecutor)
     }
+
+    override fun liveIgnoredUsers(): LiveData<List<User>> {
+        return monarchy.findAllMappedWithChanges(
+                { realm ->
+                    realm.where(IgnoredUserEntity::class.java)
+                            .isNotEmpty(IgnoredUserEntityFields.USER_ID)
+                            .sort(IgnoredUserEntityFields.USER_ID)
+                },
+                { getUser(it.userId) ?: User(userId = it.userId) }
+        )
+    }
+
+    override fun ignoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
+        val params = UpdateIgnoredUserIdsTask.Params(userIdsToIgnore = userIds.toList())
+        return updateIgnoredUserIdsTask
+                .configureWith(params) {
+                    this.callback = callback
+                }
+                .executeBy(taskExecutor)
+    }
+
+    override fun unIgnoreUserIds(userIds: List<String>, callback: MatrixCallback<Unit>): Cancelable {
+        val params = UpdateIgnoredUserIdsTask.Params(userIdsToUnIgnore = userIds.toList())
+        return updateIgnoredUserIdsTask
+                .configureWith(params) {
+                    this.callback = callback
+                }
+                .executeBy(taskExecutor)
+    }
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt
index a997c616f3..51c296ba6e 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/UserModule.kt
@@ -21,6 +21,10 @@ import dagger.Module
 import dagger.Provides
 import im.vector.matrix.android.api.session.user.UserService
 import im.vector.matrix.android.internal.session.SessionScope
+import im.vector.matrix.android.internal.session.user.accountdata.DefaultSaveIgnoredUsersTask
+import im.vector.matrix.android.internal.session.user.accountdata.DefaultUpdateIgnoredUserIdsTask
+import im.vector.matrix.android.internal.session.user.accountdata.SaveIgnoredUsersTask
+import im.vector.matrix.android.internal.session.user.accountdata.UpdateIgnoredUserIdsTask
 import im.vector.matrix.android.internal.session.user.model.DefaultSearchUserTask
 import im.vector.matrix.android.internal.session.user.model.SearchUserTask
 import retrofit2.Retrofit
@@ -43,4 +47,10 @@ internal abstract class UserModule {
 
     @Binds
     abstract fun bindSearchUserTask(searchUserTask: DefaultSearchUserTask): SearchUserTask
+
+    @Binds
+    abstract fun bindSaveIgnoredUsersTask(task: DefaultSaveIgnoredUsersTask): SaveIgnoredUsersTask
+
+    @Binds
+    abstract fun bindUpdateIgnoredUserIdsTask(task: DefaultUpdateIgnoredUserIdsTask): UpdateIgnoredUserIdsTask
 }
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/SaveIgnoredUsersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/SaveIgnoredUsersTask.kt
new file mode 100644
index 0000000000..c9a3eef440
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/SaveIgnoredUsersTask.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.matrix.android.internal.session.user.accountdata
+
+import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
+import im.vector.matrix.android.internal.task.Task
+import im.vector.matrix.android.internal.util.awaitTransaction
+import javax.inject.Inject
+
+/**
+ * Save the ignored users list in DB
+ */
+internal interface SaveIgnoredUsersTask : Task<SaveIgnoredUsersTask.Params, Unit> {
+    data class Params(
+            val userIds: List<String>
+    )
+}
+
+internal class DefaultSaveIgnoredUsersTask @Inject constructor(private val monarchy: Monarchy) : SaveIgnoredUsersTask {
+
+    override suspend fun execute(params: SaveIgnoredUsersTask.Params) {
+        monarchy.awaitTransaction { realm ->
+            // clear current ignored users
+            realm.where(IgnoredUserEntity::class.java)
+                    .findAll()
+                    .deleteAllFromRealm()
+
+            // And save the new received list
+            params.userIds.forEach { realm.createObject(IgnoredUserEntity::class.java).apply { userId = it } }
+        }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateIgnoredUserIdsTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateIgnoredUserIdsTask.kt
new file mode 100644
index 0000000000..075eeb23d1
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateIgnoredUserIdsTask.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.matrix.android.internal.session.user.accountdata
+
+import com.zhuinden.monarchy.Monarchy
+import im.vector.matrix.android.internal.database.model.IgnoredUserEntity
+import im.vector.matrix.android.internal.di.UserId
+import im.vector.matrix.android.internal.network.executeRequest
+import im.vector.matrix.android.internal.session.sync.model.accountdata.IgnoredUsersContent
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
+import im.vector.matrix.android.internal.task.Task
+import javax.inject.Inject
+
+internal interface UpdateIgnoredUserIdsTask : Task<UpdateIgnoredUserIdsTask.Params, Unit> {
+
+    data class Params(
+            val userIdsToIgnore: List<String> = emptyList(),
+            val userIdsToUnIgnore: List<String> = emptyList()
+    )
+}
+
+internal class DefaultUpdateIgnoredUserIdsTask @Inject constructor(private val accountDataApi: AccountDataAPI,
+                                                                   private val monarchy: Monarchy,
+                                                                   private val saveIgnoredUsersTask: SaveIgnoredUsersTask,
+                                                                   @UserId private val userId: String) : UpdateIgnoredUserIdsTask {
+
+    override suspend fun execute(params: UpdateIgnoredUserIdsTask.Params) {
+        // Get current list
+        val ignoredUserIds = monarchy.fetchAllMappedSync(
+                { realm -> realm.where(IgnoredUserEntity::class.java) },
+                { it.userId }
+        ).toMutableSet()
+
+        val original = ignoredUserIds.toList()
+
+        ignoredUserIds.removeAll { it in params.userIdsToUnIgnore }
+        ignoredUserIds.addAll(params.userIdsToIgnore)
+
+        if (original == ignoredUserIds) {
+            // No change
+            return
+        }
+
+        val list = ignoredUserIds.toList()
+        val body = IgnoredUsersContent.createWithUserIds(list)
+
+        executeRequest<Unit> {
+            apiCall = accountDataApi.setAccountData(userId, UserAccountData.TYPE_IGNORED_USER_LIST, body)
+        }
+
+        // Update the DB right now (do not wait for the sync to come back with updated data, for a faster UI update)
+        saveIgnoredUsersTask.execute(SaveIgnoredUsersTask.Params(list))
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
index 5c0dac1125..9fa71005ff 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/user/accountdata/UpdateUserAccountDataTask.kt
@@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.session.user.accountdata
 
 import im.vector.matrix.android.internal.di.UserId
 import im.vector.matrix.android.internal.network.executeRequest
-import im.vector.matrix.android.internal.session.sync.model.UserAccountData
+import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountData
 import im.vector.matrix.android.internal.task.Task
 import javax.inject.Inject
 
@@ -29,6 +29,7 @@ internal interface UpdateUserAccountDataTask : Task<UpdateUserAccountDataTask.Pa
         fun getData(): Any
     }
 
+    // TODO Use [UserAccountDataDirectMessages] class?
     data class DirectChatParams(override val type: String = UserAccountData.TYPE_DIRECT_MESSAGES,
                                 private val directMessages: Map<String, List<String>>
     ) : Params {
diff --git a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
index 9679c20efb..ae601bb0f3 100644
--- a/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/core/platform/VectorViewModel.kt
@@ -16,13 +16,21 @@
 
 package im.vector.riotx.core.platform
 
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
 import com.airbnb.mvrx.*
+import im.vector.riotx.core.utils.LiveEvent
 import io.reactivex.Observable
 import io.reactivex.Single
 
 abstract class VectorViewModel<S : MvRxState>(initialState: S)
     : BaseMvRxViewModel<S>(initialState, false) {
 
+    // Generic handling of any request error
+    protected val _requestErrorLiveData = MutableLiveData<LiveEvent<Throwable>>()
+    val requestErrorLiveData: LiveData<LiveEvent<Throwable>>
+        get() = _requestErrorLiveData
+
     /**
      * This method does the same thing as the execute function, but it doesn't subscribe to the stream
      * so you can use this in a switchMap or a flatMap
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt
index a219d25c09..9169183da1 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailActions.kt
@@ -50,7 +50,14 @@ sealed class RoomDetailActions {
     data class ResendMessage(val eventId: String) : RoomDetailActions()
     data class RemoveFailedEcho(val eventId: String) : RoomDetailActions()
 
-    data class ReportContent(val eventId: String, val reason: String, val spam: Boolean = false, val inappropriate: Boolean = false) : RoomDetailActions()
+    data class ReportContent(
+            val eventId: String,
+            val senderId: String?,
+            val reason: String,
+            val spam: Boolean = false,
+            val inappropriate: Boolean = false) : RoomDetailActions()
+
+    data class IgnoreUser(val userId: String?) : RoomDetailActions()
 
     object ClearSendQueue : RoomDetailActions()
     object ResendAll : RoomDetailActions()
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
index 667fa9784b..6d39a0b790 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
@@ -757,7 +757,7 @@ class RoomDetailFragment @Inject constructor(
                 .setView(layout)
                 .setPositiveButton(R.string.report_content_custom_submit) { _, _ ->
                     val reason = input.text.toString()
-                    roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, reason))
+                    roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, action.senderId, reason))
                 }
                 .setNegativeButton(R.string.cancel, null)
                 .show()
@@ -781,7 +781,9 @@ class RoomDetailFragment @Inject constructor(
                                         .setTitle(R.string.content_reported_as_spam_title)
                                         .setMessage(R.string.content_reported_as_spam_content)
                                         .setPositiveButton(R.string.ok, null)
-                                        .setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
+                                        .setNegativeButton(R.string.block_user) { _, _ ->
+                                            roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
+                                        }
                                         .show()
                                         .withColoredButton(DialogInterface.BUTTON_NEGATIVE)
                             }
@@ -790,7 +792,9 @@ class RoomDetailFragment @Inject constructor(
                                         .setTitle(R.string.content_reported_as_inappropriate_title)
                                         .setMessage(R.string.content_reported_as_inappropriate_content)
                                         .setPositiveButton(R.string.ok, null)
-                                        .setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
+                                        .setNegativeButton(R.string.block_user) { _, _ ->
+                                            roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
+                                        }
                                         .show()
                                         .withColoredButton(DialogInterface.BUTTON_NEGATIVE)
                             }
@@ -799,7 +803,9 @@ class RoomDetailFragment @Inject constructor(
                                         .setTitle(R.string.content_reported_title)
                                         .setMessage(R.string.content_reported_content)
                                         .setPositiveButton(R.string.ok, null)
-                                        .setNegativeButton(R.string.block_user) { _, _ -> vectorBaseActivity.notImplemented("block user") }
+                                        .setNegativeButton(R.string.block_user) { _, _ ->
+                                            roomDetailViewModel.process(RoomDetailActions.IgnoreUser(data.senderId))
+                                        }
                                         .show()
                                         .withColoredButton(DialogInterface.BUTTON_NEGATIVE)
                             }
@@ -1124,10 +1130,12 @@ class RoomDetailFragment @Inject constructor(
                 roomDetailViewModel.process(RoomDetailActions.RemoveFailedEcho(action.eventId))
             }
             is SimpleAction.ReportContentSpam          -> {
-                roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, "This message is spam", spam = true))
+                roomDetailViewModel.process(RoomDetailActions.ReportContent(
+                        action.eventId, action.senderId, "This message is spam", spam = true))
             }
             is SimpleAction.ReportContentInappropriate -> {
-                roomDetailViewModel.process(RoomDetailActions.ReportContent(action.eventId, "This message is inappropriate", inappropriate = true))
+                roomDetailViewModel.process(RoomDetailActions.ReportContent(
+                        action.eventId, action.senderId, "This message is inappropriate", inappropriate = true))
             }
             is SimpleAction.ReportContentCustom        -> {
                 promptReasonToReportContent(action)
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
index 2436b7a8b4..4965de4438 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt
@@ -157,6 +157,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
             is RoomDetailActions.SetReadMarkerAction         -> handleSetReadMarkerAction(action)
             is RoomDetailActions.MarkAllAsRead               -> handleMarkAllAsRead()
             is RoomDetailActions.ReportContent               -> handleReportContent(action)
+            is RoomDetailActions.IgnoreUser                  -> handleIgnoreUser(action)
         }
     }
 
@@ -710,6 +711,22 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
         })
     }
 
+    private fun handleIgnoreUser(action: RoomDetailActions.IgnoreUser) {
+        if (action.userId.isNullOrEmpty()) {
+            return
+        }
+
+        session.ignoreUserIds(listOf(action.userId), object : MatrixCallback<Unit> {
+            override fun onSuccess(data: Unit) {
+                _requestLiveData.postValue(LiveEvent(Success(action)))
+            }
+
+            override fun onFailure(failure: Throwable) {
+                _requestLiveData.postValue(LiveEvent(Fail(failure)))
+            }
+        })
+    }
+
     private fun observeSyncState() {
         session.rx()
                 .liveSyncState()
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt
index d9119f08b3..cea693c172 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsEpoxyController.kt
@@ -102,9 +102,9 @@ class MessageActionsEpoxyController @Inject constructor(private val stringProvid
             if (action is SimpleAction.ReportContent && state.expendedReportContentMenu) {
                 // Special case for report content menu: add the submenu
                 listOf(
-                        SimpleAction.ReportContentSpam(action.eventId),
-                        SimpleAction.ReportContentInappropriate(action.eventId),
-                        SimpleAction.ReportContentCustom(action.eventId)
+                        SimpleAction.ReportContentSpam(action.eventId, action.senderId),
+                        SimpleAction.ReportContentInappropriate(action.eventId, action.senderId),
+                        SimpleAction.ReportContentCustom(action.eventId, action.senderId)
                 ).forEachIndexed { indexReport, actionReport ->
                     bottomSheetItemAction {
                         id("actionReport_$indexReport")
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
index 63a4919763..63429e5def 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/MessageActionsViewModel.kt
@@ -263,7 +263,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
 
                 if (session.myUserId != event.root.senderId && event.root.getClearType() == EventType.MESSAGE) {
                     // not sent by me
-                    add(SimpleAction.ReportContent(eventId))
+                    add(SimpleAction.ReportContent(eventId, event.root.senderId))
                 }
             }
         }
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/SimpleAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/SimpleAction.kt
index 5da589d862..ab2fb8e41e 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/SimpleAction.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/action/SimpleAction.kt
@@ -22,25 +22,63 @@ import im.vector.riotx.R
 import im.vector.riotx.features.home.room.detail.timeline.item.MessageInformationData
 
 sealed class SimpleAction(@StringRes val titleRes: Int, @DrawableRes val iconResId: Int) {
-    data class AddReaction(val eventId: String) : SimpleAction(R.string.message_add_reaction, R.drawable.ic_add_reaction)
-    data class Copy(val content: String) : SimpleAction(R.string.copy, R.drawable.ic_copy)
-    data class Edit(val eventId: String) : SimpleAction(R.string.edit, R.drawable.ic_edit)
-    data class Quote(val eventId: String) : SimpleAction(R.string.quote, R.drawable.ic_quote)
-    data class Reply(val eventId: String) : SimpleAction(R.string.reply, R.drawable.ic_reply)
-    data class Share(val imageUrl: String) : SimpleAction(R.string.share, R.drawable.ic_share)
-    data class Resend(val eventId: String) : SimpleAction(R.string.global_retry, R.drawable.ic_refresh_cw)
-    data class Remove(val eventId: String) : SimpleAction(R.string.remove, R.drawable.ic_trash)
-    data class Delete(val eventId: String) : SimpleAction(R.string.delete, R.drawable.ic_delete)
-    data class Cancel(val eventId: String) : SimpleAction(R.string.cancel, R.drawable.ic_close_round)
-    data class ViewSource(val content: String) : SimpleAction(R.string.view_source, R.drawable.ic_view_source)
-    data class ViewDecryptedSource(val content: String) : SimpleAction(R.string.view_decrypted_source, R.drawable.ic_view_source)
-    data class CopyPermalink(val eventId: String) : SimpleAction(R.string.permalink, R.drawable.ic_permalink)
-    data class ReportContent(val eventId: String) : SimpleAction(R.string.report_content, R.drawable.ic_flag)
-    data class ReportContentSpam(val eventId: String) : SimpleAction(R.string.report_content_spam, R.drawable.ic_report_spam)
-    data class ReportContentInappropriate(val eventId: String) : SimpleAction(R.string.report_content_inappropriate, R.drawable.ic_report_inappropriate)
-    data class ReportContentCustom(val eventId: String) : SimpleAction(R.string.report_content_custom, R.drawable.ic_report_custom)
-    data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) : SimpleAction(0, 0)
-    data class ViewReactions(val messageInformationData: MessageInformationData) : SimpleAction(R.string.message_view_reaction, R.drawable.ic_view_reactions)
+    data class AddReaction(val eventId: String) :
+            SimpleAction(R.string.message_add_reaction, R.drawable.ic_add_reaction)
+
+    data class Copy(val content: String) :
+            SimpleAction(R.string.copy, R.drawable.ic_copy)
+
+    data class Edit(val eventId: String) :
+            SimpleAction(R.string.edit, R.drawable.ic_edit)
+
+    data class Quote(val eventId: String) :
+            SimpleAction(R.string.quote, R.drawable.ic_quote)
+
+    data class Reply(val eventId: String) :
+            SimpleAction(R.string.reply, R.drawable.ic_reply)
+
+    data class Share(val imageUrl: String) :
+            SimpleAction(R.string.share, R.drawable.ic_share)
+
+    data class Resend(val eventId: String) :
+            SimpleAction(R.string.global_retry, R.drawable.ic_refresh_cw)
+
+    data class Remove(val eventId: String) :
+            SimpleAction(R.string.remove, R.drawable.ic_trash)
+
+    data class Delete(val eventId: String) :
+            SimpleAction(R.string.delete, R.drawable.ic_delete)
+
+    data class Cancel(val eventId: String) :
+            SimpleAction(R.string.cancel, R.drawable.ic_close_round)
+
+    data class ViewSource(val content: String) :
+            SimpleAction(R.string.view_source, R.drawable.ic_view_source)
+
+    data class ViewDecryptedSource(val content: String) :
+            SimpleAction(R.string.view_decrypted_source, R.drawable.ic_view_source)
+
+    data class CopyPermalink(val eventId: String) :
+            SimpleAction(R.string.permalink, R.drawable.ic_permalink)
+
+    data class ReportContent(val eventId: String, val senderId: String?) :
+            SimpleAction(R.string.report_content, R.drawable.ic_flag)
+
+    data class ReportContentSpam(val eventId: String, val senderId: String?) :
+            SimpleAction(R.string.report_content_spam, R.drawable.ic_report_spam)
+
+    data class ReportContentInappropriate(val eventId: String, val senderId: String?) :
+            SimpleAction(R.string.report_content_inappropriate, R.drawable.ic_report_inappropriate)
+
+    data class ReportContentCustom(val eventId: String, val senderId: String?) :
+            SimpleAction(R.string.report_content_custom, R.drawable.ic_report_custom)
+
+    data class QuickReact(val eventId: String, val clickedOn: String, val add: Boolean) :
+            SimpleAction(0, 0)
+
+    data class ViewReactions(val messageInformationData: MessageInformationData) :
+            SimpleAction(R.string.message_view_reaction, R.drawable.ic_view_reactions)
+
     data class ViewEditHistory(val messageInformationData: MessageInformationData) :
             SimpleAction(R.string.message_view_edit_history, R.drawable.ic_view_edit_history)
 }
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt
index a593bb6e96..78f57dd548 100755
--- a/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt
+++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorPreferences.kt
@@ -55,8 +55,6 @@ class VectorPreferences @Inject constructor(private val context: Context) {
         const val SETTINGS_CONTACT_PREFERENCE_KEYS = "SETTINGS_CONTACT_PREFERENCE_KEYS"
         const val SETTINGS_NOTIFICATIONS_TARGETS_PREFERENCE_KEY = "SETTINGS_NOTIFICATIONS_TARGETS_PREFERENCE_KEY"
         const val SETTINGS_NOTIFICATIONS_TARGET_DIVIDER_PREFERENCE_KEY = "SETTINGS_NOTIFICATIONS_TARGET_DIVIDER_PREFERENCE_KEY"
-        const val SETTINGS_IGNORED_USERS_PREFERENCE_KEY = "SETTINGS_IGNORED_USERS_PREFERENCE_KEY"
-        const val SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY = "SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY"
         const val SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY"
         const val SETTINGS_BACKGROUND_SYNC_DIVIDER_PREFERENCE_KEY = "SETTINGS_BACKGROUND_SYNC_DIVIDER_PREFERENCE_KEY"
         const val SETTINGS_LABS_PREFERENCE_KEY = "SETTINGS_LABS_PREFERENCE_KEY"
@@ -544,7 +542,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
             MEDIA_SAVING_1_WEEK  -> System.currentTimeMillis() / 1000 - 7 * 24 * 60 * 60
             MEDIA_SAVING_1_MONTH -> System.currentTimeMillis() / 1000 - 30 * 24 * 60 * 60
             MEDIA_SAVING_FOREVER -> 0
-            else -> 0
+            else                 -> 0
         }
     }
 
@@ -559,7 +557,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
             MEDIA_SAVING_1_WEEK  -> context.getString(R.string.media_saving_period_1_week)
             MEDIA_SAVING_1_MONTH -> context.getString(R.string.media_saving_period_1_month)
             MEDIA_SAVING_FOREVER -> context.getString(R.string.media_saving_period_forever)
-            else -> "?"
+            else                 -> "?"
         }
     }
 
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsIgnoredUsersFragment.kt
deleted file mode 100644
index 32a6fe8f41..0000000000
--- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsIgnoredUsersFragment.kt
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright 2019 New Vector Ltd
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package im.vector.riotx.features.settings
-
-import androidx.appcompat.app.AlertDialog
-import androidx.preference.Preference
-import androidx.preference.PreferenceCategory
-import im.vector.riotx.R
-import im.vector.riotx.core.preference.VectorPreference
-import java.util.ArrayList
-import kotlin.Comparator
-
-class VectorSettingsIgnoredUsersFragment : VectorSettingsBaseFragment() {
-
-    override var titleRes = R.string.settings_ignored_users
-    override val preferenceXmlRes = R.xml.vector_settings_ignored_users
-
-    // displayed the ignored users list
-    private val mIgnoredUserSettingsCategoryDivider by lazy {
-        findPreference<VectorPreference>(VectorPreferences.SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY)!!
-    }
-    private val mIgnoredUserSettingsCategory by lazy {
-        findPreference<PreferenceCategory>(VectorPreferences.SETTINGS_IGNORED_USERS_PREFERENCE_KEY)!!
-    }
-
-    override fun bindPref() {
-        // Ignore users
-        refreshIgnoredUsersList()
-    }
-
-    // ==============================================================================================================
-    // ignored users list management
-    // ==============================================================================================================
-
-    /**
-     * Refresh the ignored users list
-     */
-    private fun refreshIgnoredUsersList() {
-        val ignoredUsersList = mutableListOf<String>() // TODO session.dataHandler.ignoredUserIds
-
-        ignoredUsersList.sortWith(Comparator { u1, u2 ->
-            u1.toLowerCase(VectorLocale.applicationLocale).compareTo(u2.toLowerCase(VectorLocale.applicationLocale))
-        })
-
-        val preferenceScreen = preferenceScreen
-
-        preferenceScreen.removePreference(mIgnoredUserSettingsCategory)
-        preferenceScreen.removePreference(mIgnoredUserSettingsCategoryDivider)
-        mIgnoredUserSettingsCategory.removeAll()
-
-        if (ignoredUsersList.size > 0) {
-            preferenceScreen.addPreference(mIgnoredUserSettingsCategoryDivider)
-            preferenceScreen.addPreference(mIgnoredUserSettingsCategory)
-
-            for (userId in ignoredUsersList) {
-                val preference = Preference(activity)
-
-                preference.title = userId
-                preference.key = IGNORED_USER_KEY_BASE + userId
-
-                preference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
-                    activity?.let {
-                        AlertDialog.Builder(it)
-                                .setMessage(getString(R.string.settings_unignore_user, userId))
-                                .setPositiveButton(R.string.yes) { _, _ ->
-                                    displayLoadingView()
-
-                                    val idsList = ArrayList<String>()
-                                    idsList.add(userId)
-
-                                    notImplemented()
-                                    /* TODO
-                                    session.unIgnoreUsers(idsList, object : MatrixCallback<Unit> {
-                                        override fun onSuccess(info: Void?) {
-                                            onCommonDone(null)
-                                        }
-
-                                        override fun onNetworkError(e: Exception) {
-                                            onCommonDone(e.localizedMessage)
-                                        }
-
-                                        override fun onMatrixError(e: MatrixError) {
-                                            onCommonDone(e.localizedMessage)
-                                        }
-
-                                        override fun onUnexpectedError(e: Exception) {
-                                            onCommonDone(e.localizedMessage)
-                                        }
-                                    })
-                                    */
-                                }
-                                .setNegativeButton(R.string.no, null)
-                                .show()
-                    }
-
-                    false
-                }
-
-                mIgnoredUserSettingsCategory.addPreference(preference)
-            }
-        }
-    }
-
-    companion object {
-        private const val IGNORED_USER_KEY_BASE = "IGNORED_USER_KEY_BASE"
-    }
-}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersController.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersController.kt
new file mode 100644
index 0000000000..120781874d
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersController.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.features.settings.ignored
+
+import com.airbnb.epoxy.EpoxyController
+import im.vector.matrix.android.api.session.user.model.User
+import im.vector.riotx.R
+import im.vector.riotx.core.epoxy.noResultItem
+import im.vector.riotx.core.resources.StringProvider
+import im.vector.riotx.features.home.AvatarRenderer
+import javax.inject.Inject
+
+class IgnoredUsersController @Inject constructor(private val stringProvider: StringProvider,
+                                                 private val avatarRenderer: AvatarRenderer) : EpoxyController() {
+
+    var callback: Callback? = null
+    private var viewState: IgnoredUsersViewState? = null
+
+    init {
+        requestModelBuild()
+    }
+
+    fun update(viewState: IgnoredUsersViewState) {
+        this.viewState = viewState
+        requestModelBuild()
+    }
+
+    override fun buildModels() {
+        val nonNullViewState = viewState ?: return
+        buildIgnoredUserModels(nonNullViewState.ignoredUsers)
+    }
+
+    private fun buildIgnoredUserModels(userIds: List<User>) {
+        if (userIds.isEmpty()) {
+            noResultItem {
+                id("empty")
+                text(stringProvider.getString(R.string.no_ignored_users))
+            }
+        } else {
+            userIds.forEach { userId ->
+                userItem {
+                    id(userId.userId)
+                    avatarRenderer(avatarRenderer)
+                    user(userId)
+                    itemClickAction { callback?.onUserIdClicked(userId.userId) }
+                }
+            }
+        }
+    }
+
+    interface Callback {
+        fun onUserIdClicked(userId: String)
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewModel.kt
new file mode 100644
index 0000000000..a9f142058d
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/IgnoredUsersViewModel.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.features.settings.ignored
+
+import com.airbnb.mvrx.*
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
+import im.vector.matrix.android.api.MatrixCallback
+import im.vector.matrix.android.api.session.Session
+import im.vector.matrix.android.api.session.user.model.User
+import im.vector.matrix.rx.rx
+import im.vector.riotx.core.extensions.postLiveEvent
+import im.vector.riotx.core.platform.VectorViewModel
+
+data class IgnoredUsersViewState(
+        val ignoredUsers: List<User> = emptyList(),
+        val unIgnoreRequest: Async<Unit> = Uninitialized
+) : MvRxState
+
+sealed class IgnoredUsersAction {
+    data class UnIgnore(val userId: String) : IgnoredUsersAction()
+}
+
+class IgnoredUsersViewModel @AssistedInject constructor(@Assisted initialState: IgnoredUsersViewState,
+                                                        private val session: Session) : VectorViewModel<IgnoredUsersViewState>(initialState) {
+
+    @AssistedInject.Factory
+    interface Factory {
+        fun create(initialState: IgnoredUsersViewState): IgnoredUsersViewModel
+    }
+
+    companion object : MvRxViewModelFactory<IgnoredUsersViewModel, IgnoredUsersViewState> {
+
+        @JvmStatic
+        override fun create(viewModelContext: ViewModelContext, state: IgnoredUsersViewState): IgnoredUsersViewModel? {
+            val ignoredUsersFragment: VectorSettingsIgnoredUsersFragment = (viewModelContext as FragmentViewModelContext).fragment()
+            return ignoredUsersFragment.ignoredUsersViewModelFactory.create(state)
+        }
+    }
+
+    init {
+        observeIgnoredUsers()
+    }
+
+    private fun observeIgnoredUsers() {
+        session.rx()
+                .liveIgnoredUsers()
+                .execute { async ->
+                    copy(
+                            ignoredUsers = async.invoke().orEmpty()
+                    )
+                }
+    }
+
+    fun handle(action: IgnoredUsersAction) {
+        when (action) {
+            is IgnoredUsersAction.UnIgnore -> handleUnIgnore(action)
+        }
+    }
+
+    private fun handleUnIgnore(action: IgnoredUsersAction.UnIgnore) {
+        setState {
+            copy(
+                    unIgnoreRequest = Loading()
+            )
+        }
+
+        session.unIgnoreUserIds(listOf(action.userId), object : MatrixCallback<Unit> {
+            override fun onFailure(failure: Throwable) {
+                setState {
+                    copy(
+                            unIgnoreRequest = Fail(failure)
+                    )
+                }
+
+                _requestErrorLiveData.postLiveEvent(failure)
+            }
+
+            override fun onSuccess(data: Unit) {
+                setState {
+                    copy(
+                            unIgnoreRequest = Success(data)
+                    )
+                }
+            }
+        })
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/UserItem.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/UserItem.kt
new file mode 100644
index 0000000000..a9c1b98915
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/UserItem.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package im.vector.riotx.features.settings.ignored
+
+import android.view.View
+import android.widget.ImageView
+import android.widget.TextView
+import com.airbnb.epoxy.EpoxyAttribute
+import com.airbnb.epoxy.EpoxyModelClass
+import im.vector.matrix.android.api.session.user.model.User
+import im.vector.riotx.R
+import im.vector.riotx.core.epoxy.VectorEpoxyHolder
+import im.vector.riotx.core.epoxy.VectorEpoxyModel
+import im.vector.riotx.core.extensions.setTextOrHide
+import im.vector.riotx.features.home.AvatarRenderer
+
+/**
+ * A list item for User.
+ */
+@EpoxyModelClass(layout = R.layout.item_user)
+abstract class UserItem : VectorEpoxyModel<UserItem.Holder>() {
+
+    @EpoxyAttribute
+    lateinit var avatarRenderer: AvatarRenderer
+
+    @EpoxyAttribute
+    lateinit var user: User
+
+    @EpoxyAttribute
+    var itemClickAction: (() -> Unit)? = null
+
+    override fun bind(holder: Holder) {
+        holder.root.setOnClickListener { itemClickAction?.invoke() }
+
+        avatarRenderer.render(user, holder.avatarImage)
+        holder.userIdText.setTextOrHide(user.userId)
+        holder.displayNameText.setTextOrHide(user.displayName)
+    }
+
+    class Holder : VectorEpoxyHolder() {
+        val root by bind<View>(R.id.itemUserRoot)
+        val avatarImage by bind<ImageView>(R.id.itemUserAvatar)
+        val userIdText by bind<TextView>(R.id.itemUserId)
+        val displayNameText by bind<TextView>(R.id.itemUserName)
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt
new file mode 100644
index 0000000000..11e473ae24
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/settings/ignored/VectorSettingsIgnoredUsersFragment.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.features.settings.ignored
+
+import android.os.Bundle
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
+import com.airbnb.mvrx.Async
+import com.airbnb.mvrx.Loading
+import com.airbnb.mvrx.fragmentViewModel
+import com.airbnb.mvrx.withState
+import im.vector.riotx.R
+import im.vector.riotx.core.error.ErrorFormatter
+import im.vector.riotx.core.extensions.observeEvent
+import im.vector.riotx.core.platform.VectorBaseActivity
+import im.vector.riotx.core.platform.VectorBaseFragment
+import kotlinx.android.synthetic.main.fragment_generic_recycler_epoxy.*
+import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
+import javax.inject.Inject
+
+class VectorSettingsIgnoredUsersFragment @Inject constructor(
+        val ignoredUsersViewModelFactory: IgnoredUsersViewModel.Factory,
+        private val ignoredUsersController: IgnoredUsersController,
+        private val errorFormatter: ErrorFormatter
+) : VectorBaseFragment(), IgnoredUsersController.Callback {
+
+    override fun getLayoutResId() = R.layout.fragment_generic_recycler_epoxy
+
+    private val ignoredUsersViewModel: IgnoredUsersViewModel by fragmentViewModel()
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        waiting_view_status_text.setText(R.string.please_wait)
+        waiting_view_status_text.isVisible = true
+        ignoredUsersController.callback = this
+        epoxyRecyclerView.setController(ignoredUsersController)
+        ignoredUsersViewModel.requestErrorLiveData.observeEvent(this) {
+            displayErrorDialog(it)
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+
+        (activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_ignored_users)
+    }
+
+    override fun onUserIdClicked(userId: String) {
+        AlertDialog.Builder(requireActivity())
+                .setMessage(getString(R.string.settings_unignore_user, userId))
+                .setPositiveButton(R.string.yes) { _, _ ->
+                    ignoredUsersViewModel.handle(IgnoredUsersAction.UnIgnore(userId))
+                }
+                .setNegativeButton(R.string.no, null)
+                .show()
+    }
+
+    private fun displayErrorDialog(throwable: Throwable) {
+        AlertDialog.Builder(requireActivity())
+                .setTitle(R.string.dialog_title_error)
+                .setMessage(errorFormatter.toHumanReadable(throwable))
+                .setPositiveButton(R.string.ok, null)
+                .show()
+    }
+
+    // ==============================================================================================================
+    // ignored users list management
+    // ==============================================================================================================
+
+    override fun invalidate() = withState(ignoredUsersViewModel) { state ->
+        ignoredUsersController.update(state)
+
+        handleUnIgnoreRequestStatus(state.unIgnoreRequest)
+    }
+
+    private fun handleUnIgnoreRequestStatus(unIgnoreRequest: Async<Unit>) {
+        when (unIgnoreRequest) {
+            is Loading -> waiting_view.isVisible = true
+            else       -> waiting_view.isVisible = false
+        }
+    }
+}
diff --git a/vector/src/main/res/layout/fragment_generic_recycler_epoxy.xml b/vector/src/main/res/layout/fragment_generic_recycler_epoxy.xml
index c794e8a5fa..3f02081b16 100644
--- a/vector/src/main/res/layout/fragment_generic_recycler_epoxy.xml
+++ b/vector/src/main/res/layout/fragment_generic_recycler_epoxy.xml
@@ -1,9 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.airbnb.epoxy.EpoxyRecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
-    android:id="@+id/epoxyRecyclerView"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    app:itemSpacing="1dp"
-    tools:listitem="@layout/item_pushgateway" />
+    android:layout_height="match_parent">
+
+    <com.airbnb.epoxy.EpoxyRecyclerView
+        android:id="@+id/epoxyRecyclerView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        app:itemSpacing="1dp"
+        tools:listitem="@layout/item_pushgateway" />
+
+    <include layout="@layout/merge_overlay_waiting_view" />
+
+</FrameLayout>
diff --git a/vector/src/main/res/layout/item_user.xml b/vector/src/main/res/layout/item_user.xml
new file mode 100644
index 0000000000..20e339528a
--- /dev/null
+++ b/vector/src/main/res/layout/item_user.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/itemUserRoot"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="?riotx_background"
+    android:foreground="?attr/selectableItemBackground"
+    android:gravity="center_vertical"
+    android:orientation="horizontal"
+    android:padding="8dp">
+
+    <ImageView
+        android:id="@+id/itemUserAvatar"
+        android:layout_width="40dp"
+        android:layout_height="40dp"
+        android:layout_marginStart="8dp"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:src="@tools:sample/avatars" />
+
+    <TextView
+        android:id="@+id/itemUserId"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:layout_marginStart="12dp"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:textColor="?riotx_text_primary"
+        android:textSize="15sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toTopOf="@+id/itemUserName"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/itemUserAvatar"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_chainStyle="packed"
+        tools:text="@sample/matrix.json/data/mxid" />
+
+    <TextView
+        android:id="@+id/itemUserName"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:textColor="?riotx_text_secondary"
+        android:textSize="15sp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toEndOf="@+id/itemUserAvatar"
+        app:layout_constraintTop_toBottomOf="@+id/itemUserId"
+        tools:text="@sample/matrix.json/data/displayName"
+        tools:visibility="visible" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/vector/src/main/res/layout/merge_overlay_waiting_view.xml b/vector/src/main/res/layout/merge_overlay_waiting_view.xml
index 8f09ed0988..b7e7bf41c7 100644
--- a/vector/src/main/res/layout/merge_overlay_waiting_view.xml
+++ b/vector/src/main/res/layout/merge_overlay_waiting_view.xml
@@ -3,8 +3,7 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:layout_height="match_parent">
 
     <androidx.constraintlayout.widget.ConstraintLayout
         android:id="@+id/waiting_view"
diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
index 20fc234fc8..0a2af875ab 100644
--- a/vector/src/main/res/values/strings_riotX.xml
+++ b/vector/src/main/res/values/strings_riotX.xml
@@ -4,4 +4,6 @@
     <!-- Strings not defined in Riot -->
     <string name="notice_member_no_changes">"%1$s made no changes"</string>
 
+    <string name="no_ignored_users">You are not ignoring any users</string>
+
 </resources>
diff --git a/vector/src/main/res/xml/vector_settings_ignored_users.xml b/vector/src/main/res/xml/vector_settings_ignored_users.xml
deleted file mode 100644
index 4ad8aed5b6..0000000000
--- a/vector/src/main/res/xml/vector_settings_ignored_users.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools">
-
-    <im.vector.riotx.core.preference.VectorPreferenceCategory
-        android:key="SETTINGS_IGNORED_USERS_PREFERENCE_KEY"
-        android:title="@string/settings_ignored_users" />
-
-    <im.vector.riotx.core.preference.VectorPreferenceDivider android:key="SETTINGS_IGNORE_USERS_DIVIDER_PREFERENCE_KEY" />
-
-
-</androidx.preference.PreferenceScreen>
\ No newline at end of file
diff --git a/vector/src/main/res/xml/vector_settings_root.xml b/vector/src/main/res/xml/vector_settings_root.xml
index e0cb4f778b..894784767a 100644
--- a/vector/src/main/res/xml/vector_settings_root.xml
+++ b/vector/src/main/res/xml/vector_settings_root.xml
@@ -37,10 +37,9 @@
 
     <im.vector.riotx.core.preference.VectorPreference
         android:layout_width="match_parent"
-        android:enabled="@bool/false_not_implemented"
         android:icon="@drawable/ic_settings_root_ignored_users"
         android:title="@string/settings_ignored_users"
-        app:fragment="im.vector.riotx.features.settings.VectorSettingsIgnoredUsersFragment" />
+        app:fragment="im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment" />
 
     <im.vector.riotx.core.preference.VectorPreference
         android:layout_width="match_parent"