diff --git a/CHANGES.md b/CHANGES.md
index 9af20d61d0..3918318b45 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,4 +1,4 @@
-Changes in Element 1.X.X (2020-XX-XX)
+Changes in Element 1.0.17 (2020-XX-XX)
 ===================================================
 
 Features ✨:
@@ -6,11 +6,15 @@ Features ✨:
 
 Improvements 🙌:
  - Create a WidgetItemFactory and use it for better rendering of Jitsi widget change (video conference)
+ - Open image from URL Preview (#2705)
 
 Bugfix 🐛:
  - Bug in WidgetContent.computeURL() (#2767)
  - Duplicate thumbs | Mobile reactions for 👍 and 👎 are not the same as web (#2776)
  - Join room by alias other federation error (#2778)
+ - HTML unescaping for URL preview (#2766)
+ - URL preview on reply fallback (#2756)
+ - RTL: some arrows should be rotated in RTL (#2757)
 
 Translations 🗣:
  -
@@ -25,7 +29,7 @@ Test:
  -
 
 Other changes:
- -
+ - Change app name from "Element (Riot.im)" to "Element"
 
 Changes in Element 1.0.16 (2020-02-04)
 ===================================================
@@ -53,6 +57,7 @@ Bugfix 🐛:
  - Widgets: Support $matrix_widget_id parameter (#2748)
  - Data for Worker overload (#2721)
  - Fix multiple tasks
+ - Object deletion in database is not complete (#2759)
 
 SDK API changes ⚠️:
  - Increase targetSdkVersion to 30 (#2600)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt
index c82a929ee0..1a00b85ff4 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/ContentUtils.kt
@@ -20,7 +20,7 @@ object ContentUtils {
         val lines = repliedBody.lines()
         var wellFormed = repliedBody.startsWith(">")
         var endOfPreviousFound = false
-        val usefullines = ArrayList<String>()
+        val usefulLines = ArrayList<String>()
         lines.forEach {
             if (it == "") {
                 endOfPreviousFound = true
@@ -29,10 +29,10 @@ object ContentUtils {
             if (!endOfPreviousFound) {
                 wellFormed = wellFormed && it.startsWith(">")
             } else {
-                usefullines.add(it)
+                usefulLines.add(it)
             }
         }
-        return usefullines.joinToString("\n").takeIf { wellFormed } ?: repliedBody
+        return usefulLines.joinToString("\n").takeIf { wellFormed } ?: repliedBody
     }
 
     fun extractUsefulTextFromHtmlReply(repliedBody: String): String {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt
index e494cb5b31..cf2d6aa269 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt
@@ -21,11 +21,11 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
 import org.matrix.android.sdk.internal.util.JsonCanonicalizer
 import timber.log.Timber
 
-fun CryptoDeviceInfo.canonicalSignable(): String {
+internal fun CryptoDeviceInfo.canonicalSignable(): String {
     return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
 }
 
-fun CryptoCrossSigningKey.canonicalSignable(): String {
+internal fun CryptoCrossSigningKey.canonicalSignable(): String {
     return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary())
 }
 
@@ -40,7 +40,7 @@ fun String.fromBase64(): ByteArray {
 /**
  * Decode the base 64. Return null in case of bad format. Should be used when parsing received data from external source
  */
-fun String.fromBase64Safe(): ByteArray? {
+internal fun String.fromBase64Safe(): ByteArray? {
     return try {
         Base64.decode(this, Base64.DEFAULT)
     } catch (throwable: Throwable) {
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt
index 202aa55624..606d2e3fc0 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/CryptoCrossSigningKey.kt
@@ -63,7 +63,7 @@ data class CryptoCrossSigningKey(
         )
     }
 
-    data class Builder(
+    internal data class Builder(
             val userId: String,
             val usage: KeyUsage,
             private var base64Pkey: String? = null,
@@ -97,7 +97,7 @@ data class CryptoCrossSigningKey(
     }
 }
 
-enum class KeyUsage(val value: String) {
+internal enum class KeyUsage(val value: String) {
     MASTER("master"),
     SELF_SIGNING("self_signing"),
     USER_SIGNING("user_signing")
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt
index 369a4976c9..b9213ba758 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt
@@ -83,6 +83,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
 import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
 import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity
 import org.matrix.android.sdk.internal.crypto.store.db.model.createPrimaryKey
+import org.matrix.android.sdk.internal.crypto.store.db.model.deleteOnCascade
 import org.matrix.android.sdk.internal.crypto.store.db.query.create
 import org.matrix.android.sdk.internal.crypto.store.db.query.delete
 import org.matrix.android.sdk.internal.crypto.store.db.query.get
@@ -94,6 +95,7 @@ import org.matrix.android.sdk.internal.di.CryptoDatabase
 import org.matrix.android.sdk.internal.di.DeviceId
 import org.matrix.android.sdk.internal.di.MoshiProvider
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.extensions.clearWith
 import org.matrix.android.sdk.internal.session.SessionScope
 import org.matrix.olm.OlmAccount
 import org.matrix.olm.OlmException
@@ -293,7 +295,7 @@ internal class RealmCryptoStore @Inject constructor(
                                 realm.insertOrUpdate(entity)
                             }
                             // Ensure all other devices are deleted
-                            u.devices.deleteAllFromRealm()
+                            u.devices.clearWith { it.deleteOnCascade() }
                             u.devices.addAll(new)
                         }
             }
@@ -309,7 +311,7 @@ internal class RealmCryptoStore @Inject constructor(
                     .let { userEntity ->
                         if (masterKey == null || selfSigningKey == null) {
                             // The user has disabled cross signing?
-                            userEntity.crossSigningInfoEntity?.deleteFromRealm()
+                            userEntity.crossSigningInfoEntity?.deleteOnCascade()
                             userEntity.crossSigningInfoEntity = null
                         } else {
                             var shouldResetMyDevicesLocalTrust = false
@@ -1633,7 +1635,7 @@ internal class RealmCryptoStore @Inject constructor(
         } else {
             // Just override existing, caller should check and untrust id needed
             val existing = CrossSigningInfoEntity.getOrCreate(realm, userId)
-            existing.crossSigningKeys.deleteAllFromRealm()
+            existing.crossSigningKeys.clearWith { it.deleteOnCascade() }
             existing.crossSigningKeys.addAll(
                     info.crossSigningKeys.map {
                         crossSigningKeysMapper.map(it)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt
index fdd3e94754..8599c972e9 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/CrossSigningInfoEntity.kt
@@ -16,10 +16,11 @@
 
 package org.matrix.android.sdk.internal.crypto.store.db.model
 
-import org.matrix.android.sdk.internal.crypto.model.KeyUsage
 import io.realm.RealmList
 import io.realm.RealmObject
 import io.realm.annotations.PrimaryKey
+import org.matrix.android.sdk.internal.crypto.model.KeyUsage
+import org.matrix.android.sdk.internal.extensions.clearWith
 
 internal open class CrossSigningInfoEntity(
         @PrimaryKey
@@ -56,3 +57,8 @@ internal open class CrossSigningInfoEntity(
         info?.let { crossSigningKeys.add(it) }
     }
 }
+
+internal fun CrossSigningInfoEntity.deleteOnCascade() {
+    crossSigningKeys.clearWith { it.deleteOnCascade() }
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt
index 571b9bb05f..61870ec486 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/DeviceInfoEntity.kt
@@ -47,3 +47,8 @@ internal open class DeviceInfoEntity(
 
     companion object
 }
+
+internal fun DeviceInfoEntity.deleteOnCascade() {
+    trustLevelEntity?.deleteFromRealm()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt
index 8f2357223e..9133413589 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/KeyInfoEntity.kt
@@ -30,3 +30,8 @@ internal open class KeyInfoEntity(
         var signatures: String? = null,
         var trustLevelEntity: TrustLevelEntity? = null
 ) : RealmObject()
+
+internal fun KeyInfoEntity.deleteOnCascade() {
+    trustLevelEntity?.deleteFromRealm()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt
index 52c30a27cc..df9482bf96 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/UserEntity.kt
@@ -19,13 +19,20 @@ package org.matrix.android.sdk.internal.crypto.store.db.model
 import io.realm.RealmList
 import io.realm.RealmObject
 import io.realm.annotations.PrimaryKey
+import org.matrix.android.sdk.internal.extensions.clearWith
 
 internal open class UserEntity(
         @PrimaryKey var userId: String? = null,
         var devices: RealmList<DeviceInfoEntity> = RealmList(),
         var crossSigningInfoEntity: CrossSigningInfoEntity? = null,
-        var deviceTrackingStatus: Int = 0)
-    : RealmObject() {
+        var deviceTrackingStatus: Int = 0
+) : RealmObject() {
 
     companion object
 }
+
+internal fun UserEntity.deleteOnCascade() {
+    devices.clearWith { it.deleteOnCascade() }
+    crossSigningInfoEntity?.deleteOnCascade()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt
index a1f8bd7262..5a3b8e5397 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/UserEntitiesQueries.kt
@@ -16,11 +16,12 @@
 
 package org.matrix.android.sdk.internal.crypto.store.db.query
 
-import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
-import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
 import io.realm.Realm
 import io.realm.kotlin.createObject
 import io.realm.kotlin.where
+import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
+import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
+import org.matrix.android.sdk.internal.crypto.store.db.model.deleteOnCascade
 
 /**
  * Get or create a user
@@ -39,5 +40,5 @@ internal fun UserEntity.Companion.delete(realm: Realm, userId: String) {
     realm.where<UserEntity>()
             .equalTo(UserEntityFields.USER_ID, userId)
             .findFirst()
-            ?.deleteFromRealm()
+            ?.deleteOnCascade()
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt
index 4c1e896a21..052b3f4e72 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/tools/Tools.kt
@@ -21,7 +21,7 @@ import org.matrix.olm.OlmPkEncryption
 import org.matrix.olm.OlmPkSigning
 import org.matrix.olm.OlmUtility
 
-fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
+internal fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
     val olmPkEncryption = OlmPkEncryption()
     try {
         return block(olmPkEncryption)
@@ -30,7 +30,7 @@ fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
     }
 }
 
-fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T {
+internal fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T {
     val olmPkDecryption = OlmPkDecryption()
     try {
         return block(olmPkDecryption)
@@ -39,7 +39,7 @@ fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T {
     }
 }
 
-fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T {
+internal fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T {
     val olmPkSigning = OlmPkSigning()
     try {
         return block(olmPkSigning)
@@ -48,7 +48,7 @@ fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T {
     }
 }
 
-fun <T> withOlmUtility(block: (OlmUtility) -> T): T {
+internal fun <T> withOlmUtility(block: (OlmUtility) -> T): T {
     val olmUtility = OlmUtility()
     try {
         return block(olmUtility)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt
index e305c7ea38..8b4ce6106b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/DatabaseCleaner.kt
@@ -16,6 +16,10 @@
 
 package org.matrix.android.sdk.internal.database
 
+import io.realm.Realm
+import io.realm.RealmConfiguration
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
 import org.matrix.android.sdk.internal.database.helper.nextDisplayIndex
 import org.matrix.android.sdk.internal.database.model.ChunkEntity
 import org.matrix.android.sdk.internal.database.model.ChunkEntityFields
@@ -23,14 +27,11 @@ import org.matrix.android.sdk.internal.database.model.EventEntity
 import org.matrix.android.sdk.internal.database.model.RoomEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
+import org.matrix.android.sdk.internal.database.model.deleteOnCascade
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.session.SessionLifecycleObserver
 import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection
 import org.matrix.android.sdk.internal.task.TaskExecutor
-import io.realm.Realm
-import io.realm.RealmConfiguration
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -56,7 +57,7 @@ internal class DatabaseCleaner @Inject constructor(@SessionDatabase private val
         }
     }
 
-    private suspend fun cleanUp(realm: Realm, threshold: Long) {
+    private fun cleanUp(realm: Realm, threshold: Long) {
         val numberOfEvents = realm.where(EventEntity::class.java).findAll().size
         val numberOfTimelineEvents = realm.where(TimelineEventEntity::class.java).findAll().size
         Timber.v("Number of events in db: $numberOfEvents | Number of timeline events in db: $numberOfTimelineEvents")
@@ -76,20 +77,7 @@ internal class DatabaseCleaner @Inject constructor(@SessionDatabase private val
                 chunk.numberOfTimelineEvents = chunk.numberOfTimelineEvents - eventsToRemove.size
                 eventsToRemove.forEach {
                     val canDeleteRoot = it.root?.stateKey == null
-                    if (canDeleteRoot) {
-                        it.root?.deleteFromRealm()
-                    }
-                    it.readReceipts?.readReceipts?.deleteAllFromRealm()
-                    it.readReceipts?.deleteFromRealm()
-                    it.annotations?.apply {
-                        editSummary?.deleteFromRealm()
-                        pollResponseSummary?.deleteFromRealm()
-                        referencesSummaryEntity?.deleteFromRealm()
-                        reactionsSummary.deleteAllFromRealm()
-                    }
-                    it.annotations?.deleteFromRealm()
-                    it.readReceipts?.deleteFromRealm()
-                    it.deleteFromRealm()
+                    it.deleteOnCascade(canDeleteRoot)
                 }
                 // We reset the prevToken so we will need to fetch again.
                 chunk.prevToken = null
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt
index f764c4da4b..b4935cfdcc 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt
@@ -38,12 +38,6 @@ import io.realm.Sort
 import io.realm.kotlin.createObject
 import timber.log.Timber
 
-internal fun ChunkEntity.deleteOnCascade() {
-    assertIsManaged()
-    this.timelineEvents.deleteAllFromRealm()
-    this.deleteFromRealm()
-}
-
 internal fun ChunkEntity.merge(roomId: String, chunkToMerge: ChunkEntity, direction: PaginationDirection) {
     assertIsManaged()
     val localRealm = this.realm
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt
index a4108f0966..724f307e3b 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/RoomEntityHelper.kt
@@ -19,12 +19,7 @@ package org.matrix.android.sdk.internal.database.helper
 import org.matrix.android.sdk.internal.database.model.ChunkEntity
 import org.matrix.android.sdk.internal.database.model.RoomEntity
 
-internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) {
-    chunks.remove(chunkEntity)
-    chunkEntity.deleteOnCascade()
-}
-
-internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) {
+internal fun RoomEntity.addIfNecessary(chunkEntity: ChunkEntity) {
     if (!chunks.contains(chunkEntity)) {
         chunks.add(chunkEntity)
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt
index 6f4dac182c..90e867749e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/TimelineEventEntityHelper.kt
@@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.database.helper
 
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntity
 import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields
-import org.matrix.android.sdk.internal.extensions.assertIsManaged
 import io.realm.Realm
 
 internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long {
@@ -29,11 +28,3 @@ internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long {
         currentIdNum.toLong() + 1
     }
 }
-
-internal fun TimelineEventEntity.deleteOnCascade() {
-    assertIsManaged()
-    root?.deleteFromRealm()
-    annotations?.deleteFromRealm()
-    readReceipts?.deleteFromRealm()
-    deleteFromRealm()
-}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt
index 9770352a95..68533a3c19 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ChunkEntity.kt
@@ -21,6 +21,8 @@ import io.realm.RealmObject
 import io.realm.RealmResults
 import io.realm.annotations.Index
 import io.realm.annotations.LinkingObjects
+import org.matrix.android.sdk.internal.extensions.assertIsManaged
+import org.matrix.android.sdk.internal.extensions.clearWith
 
 internal open class ChunkEntity(@Index var prevToken: String? = null,
         // Because of gaps we can have several chunks with nextToken == null
@@ -43,3 +45,12 @@ internal open class ChunkEntity(@Index var prevToken: String? = null,
 
     companion object
 }
+
+internal fun ChunkEntity.deleteOnCascade(deleteStateEvents: Boolean, canDeleteRoot: Boolean) {
+    assertIsManaged()
+    if (deleteStateEvents) {
+        stateEvents.deleteAllFromRealm()
+    }
+    timelineEvents.clearWith { it.deleteOnCascade(canDeleteRoot) }
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt
index 3e5e277613..33f26d439f 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/EventAnnotationsSummaryEntity.kt
@@ -31,3 +31,11 @@ internal open class EventAnnotationsSummaryEntity(
 
     companion object
 }
+
+internal fun EventAnnotationsSummaryEntity.deleteOnCascade() {
+    reactionsSummary.deleteAllFromRealm()
+    editSummary?.deleteFromRealm()
+    referencesSummaryEntity?.deleteFromRealm()
+    pollResponseSummary?.deleteFromRealm()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt
index 85375c8064..5bfe2833f8 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRuleEntity.kt
@@ -40,3 +40,8 @@ internal open class PushRuleEntity(
 
     companion object
 }
+
+internal fun PushRuleEntity.deleteOnCascade() {
+    conditions?.deleteAllFromRealm()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt
index 21e3510cd2..571bc71c27 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PushRulesEntity.kt
@@ -18,6 +18,7 @@ package org.matrix.android.sdk.internal.database.model
 import org.matrix.android.sdk.api.pushrules.RuleKind
 import io.realm.RealmList
 import io.realm.RealmObject
+import org.matrix.android.sdk.internal.extensions.clearWith
 
 internal open class PushRulesEntity(
         var scope: String = "",
@@ -35,3 +36,8 @@ internal open class PushRulesEntity(
 
     companion object
 }
+
+internal fun PushRulesEntity.deleteOnCascade() {
+    pushRules.clearWith { it.deleteOnCascade() }
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt
index f85c01c48a..af8e4f2d37 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/PusherEntity.kt
@@ -15,8 +15,8 @@
  */
 package org.matrix.android.sdk.internal.database.model
 
-import org.matrix.android.sdk.api.session.pushers.PusherState
 import io.realm.RealmObject
+import org.matrix.android.sdk.api.session.pushers.PusherState
 
 // TODO
 //        at java.lang.Thread.run(Thread.java:764)
@@ -54,3 +54,8 @@ internal open class PusherEntity(
 
     companion object
 }
+
+internal fun PusherEntity.deleteOnCascade() {
+    data?.deleteFromRealm()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt
index 98b4329076..9ca4adc33e 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/ReadReceiptsSummaryEntity.kt
@@ -34,3 +34,8 @@ internal open class ReadReceiptsSummaryEntity(
 
     companion object
 }
+
+internal fun ReadReceiptsSummaryEntity.deleteOnCascade() {
+    readReceipts.deleteAllFromRealm()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt
index 7bd0dbbb8f..30bbde70c2 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/model/TimelineEventEntity.kt
@@ -20,6 +20,7 @@ import io.realm.RealmObject
 import io.realm.RealmResults
 import io.realm.annotations.Index
 import io.realm.annotations.LinkingObjects
+import org.matrix.android.sdk.internal.extensions.assertIsManaged
 
 internal open class TimelineEventEntity(var localId: Long = 0,
                                         @Index var eventId: String = "",
@@ -39,3 +40,13 @@ internal open class TimelineEventEntity(var localId: Long = 0,
 
     companion object
 }
+
+internal fun TimelineEventEntity.deleteOnCascade(canDeleteRoot: Boolean) {
+    assertIsManaged()
+    if (canDeleteRoot) {
+        root?.deleteFromRealm()
+    }
+    annotations?.deleteOnCascade()
+    readReceipts?.deleteOnCascade()
+    deleteFromRealm()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt
index 0718096fd5..e52e32e16a 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/extensions/RealmExtensions.kt
@@ -16,8 +16,18 @@
 
 package org.matrix.android.sdk.internal.extensions
 
+import io.realm.RealmList
 import io.realm.RealmObject
 
 internal fun RealmObject.assertIsManaged() {
     check(isManaged) { "${javaClass.simpleName} entity should be managed to use this function" }
 }
+
+/**
+ * Clear a RealmList by deleting all its items calling the provided lambda
+ */
+internal fun <T> RealmList<T>.clearWith(delete: (T) -> Unit) {
+    while (!isEmpty()) {
+        first()?.let { delete.invoke(it) }
+    }
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
index a218f3f93c..d85e471f1d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/GetPreviewUrlTask.kt
@@ -28,6 +28,7 @@ import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
+import org.matrix.android.sdk.internal.util.unescapeHtml
 import java.util.Date
 import javax.inject.Inject
 
@@ -73,9 +74,9 @@ internal class DefaultGetPreviewUrlTask @Inject constructor(
     private fun JsonDict.toPreviewUrlData(url: String): PreviewUrlData {
         return PreviewUrlData(
                 url = (get("og:url") as? String) ?: url,
-                siteName = get("og:site_name") as? String,
-                title = get("og:title") as? String,
-                description = get("og:description") as? String,
+                siteName = (get("og:site_name") as? String)?.unescapeHtml(),
+                title = (get("og:title") as? String)?.unescapeHtml(),
+                description = (get("og:description") as? String)?.unescapeHtml(),
                 mxcUrl = get("og:image") as? String
         )
     }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
index 6137b4152c..d1fb5b98ff 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/media/UrlsExtractor.kt
@@ -21,6 +21,8 @@ import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.room.model.message.MessageType
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
 import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
+import org.matrix.android.sdk.api.session.room.timeline.isReply
+import org.matrix.android.sdk.api.util.ContentUtils
 import javax.inject.Inject
 
 internal class UrlsExtractor @Inject constructor() {
@@ -35,7 +37,14 @@ internal class UrlsExtractor @Inject constructor() {
                             || it.msgType == MessageType.MSGTYPE_NOTICE
                             || it.msgType == MessageType.MSGTYPE_EMOTE
                 }
-                ?.body
+                ?.let { messageContent ->
+                    if (event.isReply()) {
+                        // This is a reply, strip the reply fallback
+                        ContentUtils.extractUsefulTextFromReply(messageContent.body)
+                    } else {
+                        messageContent.body
+                    }
+                }
                 ?.let { urlRegex.findAll(it) }
                 ?.map { it.value }
                 ?.filter { it.startsWith("https://") || it.startsWith("http://") }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt
index 4c7d370446..125c8f0022 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/GetPushersTask.kt
@@ -19,6 +19,7 @@ import com.zhuinden.monarchy.Monarchy
 import org.matrix.android.sdk.api.session.pushers.PusherState
 import org.matrix.android.sdk.internal.database.mapper.toEntity
 import org.matrix.android.sdk.internal.database.model.PusherEntity
+import org.matrix.android.sdk.internal.database.model.deleteOnCascade
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
 import org.matrix.android.sdk.internal.network.executeRequest
@@ -41,7 +42,8 @@ internal class DefaultGetPushersTask @Inject constructor(
         monarchy.awaitTransaction { realm ->
             // clear existings?
             realm.where(PusherEntity::class.java)
-                    .findAll().deleteAllFromRealm()
+                    .findAll()
+                    .forEach { it.deleteOnCascade() }
             response.pushers?.forEach { jsonPusher ->
                 jsonPusher.toEntity().also {
                     it.state = PusherState.REGISTERED
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt
index 6ba769a3b7..6a4b891ecf 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/SavePushRulesTask.kt
@@ -21,6 +21,7 @@ import org.matrix.android.sdk.api.pushrules.RuleSetKey
 import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse
 import org.matrix.android.sdk.internal.database.mapper.PushRulesMapper
 import org.matrix.android.sdk.internal.database.model.PushRulesEntity
+import org.matrix.android.sdk.internal.database.model.deleteOnCascade
 import org.matrix.android.sdk.internal.di.SessionDatabase
 import org.matrix.android.sdk.internal.task.Task
 import org.matrix.android.sdk.internal.util.awaitTransaction
@@ -40,7 +41,7 @@ internal class DefaultSavePushRulesTask @Inject constructor(@SessionDatabase pri
             // clear current push rules
             realm.where(PushRulesEntity::class.java)
                     .findAll()
-                    .deleteAllFromRealm()
+                    .forEach { it.deleteOnCascade() }
 
             // Save only global rules for the moment
             val globalRules = params.pushRules.global
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
index 1a497b8835..c38dcd00a7 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/TokenChunkEventPersistor.kt
@@ -22,16 +22,16 @@ import org.matrix.android.sdk.api.session.events.model.EventType
 import org.matrix.android.sdk.api.session.events.model.toModel
 import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
 import org.matrix.android.sdk.api.session.room.send.SendState
-import org.matrix.android.sdk.internal.database.helper.addOrUpdate
+import org.matrix.android.sdk.internal.database.helper.addIfNecessary
 import org.matrix.android.sdk.internal.database.helper.addStateEvent
 import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
-import org.matrix.android.sdk.internal.database.helper.deleteOnCascade
 import org.matrix.android.sdk.internal.database.helper.merge
 import org.matrix.android.sdk.internal.database.mapper.toEntity
 import org.matrix.android.sdk.internal.database.model.ChunkEntity
 import org.matrix.android.sdk.internal.database.model.EventInsertType
 import org.matrix.android.sdk.internal.database.model.RoomEntity
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
+import org.matrix.android.sdk.internal.database.model.deleteOnCascade
 import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
 import org.matrix.android.sdk.internal.database.query.create
 import org.matrix.android.sdk.internal.database.query.find
@@ -172,7 +172,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
             val currentLastForwardChunk = ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)
             if (currentChunk != currentLastForwardChunk) {
                 currentChunk.isLastForward = true
-                currentLastForwardChunk?.deleteOnCascade()
+                currentLastForwardChunk?.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = false)
                 RoomSummaryEntity.where(realm, roomId).findFirst()?.apply {
                     latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
                 }
@@ -235,7 +235,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
             }
         }
         chunksToDelete.forEach {
-            it.deleteOnCascade()
+            it.deleteOnCascade(deleteStateEvents = false, canDeleteRoot = false)
         }
         val roomSummaryEntity = RoomSummaryEntity.getOrCreate(realm, roomId)
         val shouldUpdateSummary = roomSummaryEntity.latestPreviewableEvent == null
@@ -244,7 +244,7 @@ internal class TokenChunkEventPersistor @Inject constructor(@SessionDatabase pri
             roomSummaryEntity.latestPreviewableEvent = RoomSummaryEventsHelper.getLatestPreviewableEvent(realm, roomId)
         }
         if (currentChunk.isValid) {
-            RoomEntity.where(realm, roomId).findFirst()?.addOrUpdate(currentChunk)
+            RoomEntity.where(realm, roomId).findFirst()?.addIfNecessary(currentChunk)
         }
     }
 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt
index 456b0f9c26..6d1b3ae034 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/RoomSyncHandler.kt
@@ -30,9 +30,8 @@ import org.matrix.android.sdk.api.session.room.send.SendState
 import org.matrix.android.sdk.internal.crypto.DefaultCryptoService
 import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
 import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
-import org.matrix.android.sdk.internal.database.helper.addOrUpdate
+import org.matrix.android.sdk.internal.database.helper.addIfNecessary
 import org.matrix.android.sdk.internal.database.helper.addTimelineEvent
-import org.matrix.android.sdk.internal.database.helper.deleteOnCascade
 import org.matrix.android.sdk.internal.database.mapper.asDomain
 import org.matrix.android.sdk.internal.database.mapper.toEntity
 import org.matrix.android.sdk.internal.database.model.ChunkEntity
@@ -40,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntity
 import org.matrix.android.sdk.internal.database.model.EventInsertType
 import org.matrix.android.sdk.internal.database.model.RoomEntity
 import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity
+import org.matrix.android.sdk.internal.database.model.deleteOnCascade
 import org.matrix.android.sdk.internal.database.query.copyToRealmOrIgnore
 import org.matrix.android.sdk.internal.database.query.find
 import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom
@@ -48,6 +48,7 @@ import org.matrix.android.sdk.internal.database.query.getOrNull
 import org.matrix.android.sdk.internal.database.query.where
 import org.matrix.android.sdk.internal.di.MoshiProvider
 import org.matrix.android.sdk.internal.di.UserId
+import org.matrix.android.sdk.internal.extensions.clearWith
 import org.matrix.android.sdk.internal.session.DefaultInitialSyncProgressService
 import org.matrix.android.sdk.internal.session.mapWithProgress
 import org.matrix.android.sdk.internal.session.room.membership.RoomChangeMembershipStateDataSource
@@ -175,7 +176,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
                     syncLocalTimestampMillis,
                     isInitialSync
             )
-            roomEntity.addOrUpdate(chunkEntity)
+            roomEntity.addIfNecessary(chunkEntity)
         }
         val hasRoomMember = roomSync.state?.events?.firstOrNull {
             it.type == EventType.STATE_ROOM_MEMBER
@@ -263,7 +264,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
         val leftMember = RoomMemberSummaryEntity.where(realm, roomId, userId).findFirst()
         val membership = leftMember?.membership ?: Membership.LEAVE
         roomEntity.membership = membership
-        roomEntity.chunks.deleteAllFromRealm()
+        roomEntity.chunks.clearWith { it.deleteOnCascade(deleteStateEvents = true, canDeleteRoot = true) }
         roomTypingUsersHandler.handle(realm, roomId, null)
         roomChangeMembershipStateDataSource.setMembershipFromSync(roomId, Membership.LEAVE)
         roomSummaryUpdater.update(realm, roomId, membership, roomSync.summary, roomSync.unreadNotifications)
@@ -340,7 +341,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
                         }
                     }
                     // Finally delete the local echo
-                    sendingEventEntity.deleteOnCascade()
+                    sendingEventEntity.deleteOnCascade(true)
                 } else {
                     Timber.v("Can't find corresponding local echo for tx:$it")
                 }
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt
index 0e549172f3..449d47abe5 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/sync/UserAccountDataSyncHandler.kt
@@ -16,8 +16,10 @@
 
 package org.matrix.android.sdk.internal.session.sync
 
-import com.squareup.moshi.Moshi
 import com.zhuinden.monarchy.Monarchy
+import io.realm.Realm
+import io.realm.RealmList
+import io.realm.kotlin.where
 import org.matrix.android.sdk.api.pushrules.RuleScope
 import org.matrix.android.sdk.api.pushrules.RuleSetKey
 import org.matrix.android.sdk.api.pushrules.rest.GetPushRulesResponse
@@ -37,6 +39,7 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity
 import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields
 import org.matrix.android.sdk.internal.database.model.UserAccountDataEntity
 import org.matrix.android.sdk.internal.database.model.UserAccountDataEntityFields
+import org.matrix.android.sdk.internal.database.model.deleteOnCascade
 import org.matrix.android.sdk.internal.database.query.getDirectRooms
 import org.matrix.android.sdk.internal.database.query.getOrCreate
 import org.matrix.android.sdk.internal.database.query.where
@@ -50,9 +53,6 @@ import org.matrix.android.sdk.internal.session.sync.model.accountdata.IgnoredUse
 import org.matrix.android.sdk.internal.session.sync.model.accountdata.UserAccountDataSync
 import org.matrix.android.sdk.internal.session.user.accountdata.DirectChatsHelper
 import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
-import io.realm.Realm
-import io.realm.RealmList
-import io.realm.kotlin.where
 import timber.log.Timber
 import javax.inject.Inject
 
@@ -60,7 +60,6 @@ internal class UserAccountDataSyncHandler @Inject constructor(
         @SessionDatabase private val monarchy: Monarchy,
         @UserId private val userId: String,
         private val directChatsHelper: DirectChatsHelper,
-        private val moshi: Moshi,
         private val updateUserAccountDataTask: UpdateUserAccountDataTask) {
 
     fun handle(realm: Realm, accountData: UserAccountDataSync?) {
@@ -113,7 +112,7 @@ internal class UserAccountDataSyncHandler @Inject constructor(
         val pushRules = event.content.toModel<GetPushRulesResponse>() ?: return
         realm.where(PushRulesEntity::class.java)
                 .findAll()
-                .deleteAllFromRealm()
+                .forEach { it.deleteOnCascade() }
 
         // Save only global rules for the moment
         val globalRules = pushRules.global
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Html.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Html.kt
new file mode 100644
index 0000000000..329b100497
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/util/Html.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 The Matrix.org Foundation C.I.C.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.matrix.android.sdk.internal.util
+
+import androidx.core.text.HtmlCompat
+
+internal fun String.unescapeHtml(): String {
+    return HtmlCompat.fromHtml(this, HtmlCompat.FROM_HTML_MODE_LEGACY).toString()
+}
diff --git a/vector/build.gradle b/vector/build.gradle
index 95f4238a7c..486e1b0ab8 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -210,7 +210,7 @@ android {
         }
 
         release {
-            resValue "string", "app_name", "Element (Riot.im)"
+            resValue "string", "app_name", "Element"
 
             resValue "bool", "debug_mode", "false"
             buildConfigField "boolean", "LOW_PRIVACY_LOG_ENABLE", "false"
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
index 2d2059377c..aeb1c30f4b 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt
@@ -1687,6 +1687,10 @@ class RoomDetailFragment @Inject constructor(
         roomDetailViewModel.handle(RoomDetailAction.DoNotShowPreviewUrlFor(eventId, url))
     }
 
+    override fun onPreviewUrlImageClicked(sharedView: View?, mxcUrl: String?, title: String?) {
+        navigator.openBigImageViewer(requireActivity(), sharedView, mxcUrl, title)
+    }
+
     private fun onShareActionClicked(action: EventSharedAction.Share) {
         if (action.messageContent is MessageTextContent) {
             shareText(requireContext(), action.messageContent.body)
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
index 588b7783e2..7eedd5ca8e 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt
@@ -87,12 +87,12 @@ import org.matrix.android.sdk.api.session.room.read.ReadService
 import org.matrix.android.sdk.api.session.room.send.UserDraft
 import org.matrix.android.sdk.api.session.room.timeline.Timeline
 import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
+import org.matrix.android.sdk.api.session.room.timeline.getRelationContent
 import org.matrix.android.sdk.api.session.room.timeline.getTextEditableContent
 import org.matrix.android.sdk.api.session.widgets.model.Widget
 import org.matrix.android.sdk.api.session.widgets.model.WidgetType
 import org.matrix.android.sdk.api.util.appendParamToUrl
 import org.matrix.android.sdk.api.util.toOptional
-import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
 import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
 import org.matrix.android.sdk.internal.util.awaitCallback
 import org.matrix.android.sdk.rx.rx
@@ -754,8 +754,7 @@ class RoomDetailViewModel @AssistedInject constructor(
                 }
                 is SendMode.EDIT    -> {
                     // is original event a reply?
-                    val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
-                            ?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
+                    val inReplyTo = state.sendMode.timelineEvent.getRelationContent()?.inReplyTo?.eventId
                     if (inReplyTo != null) {
                         // TODO check if same content?
                         room.getTimeLineEvent(inReplyTo)?.let {
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt
index ba3ffe3174..1e108a2062 100644
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt
@@ -130,6 +130,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
     interface PreviewUrlCallback {
         fun onPreviewUrlClicked(url: String)
         fun onPreviewUrlCloseClicked(eventId: String, url: String)
+        fun onPreviewUrlImageClicked(sharedView: View?, mxcUrl: String?, title: String?)
     }
 
     // Map eventId to adapter position
diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt
index a36b1281ba..f5d09efd18 100755
--- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt
+++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/url/PreviewUrlView.kt
@@ -23,7 +23,7 @@ import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.core.view.isVisible
 import im.vector.app.R
 import im.vector.app.core.extensions.setTextOrHide
-import im.vector.app.databinding.UrlPreviewBinding
+import im.vector.app.databinding.ViewUrlPreviewBinding
 import im.vector.app.features.home.room.detail.timeline.TimelineEventController
 import im.vector.app.features.media.ImageContentRenderer
 
@@ -39,7 +39,7 @@ class PreviewUrlView @JvmOverloads constructor(
         defStyleAttr: Int = 0
 ) : ConstraintLayout(context, attrs, defStyleAttr), View.OnClickListener {
 
-    private lateinit var views: UrlPreviewBinding
+    private lateinit var views: ViewUrlPreviewBinding
 
     var delegate: TimelineEventController.PreviewUrlCallback? = null
 
@@ -80,6 +80,19 @@ class PreviewUrlView @JvmOverloads constructor(
         }
     }
 
+    private fun onImageClick() {
+        when (val finalState = state) {
+            is PreviewUrlUiState.Data -> {
+                delegate?.onPreviewUrlImageClicked(
+                        sharedView = views.urlPreviewImage,
+                        mxcUrl = finalState.previewUrlData.mxcUrl,
+                        title = finalState.previewUrlData.title
+                )
+            }
+            else                      -> Unit
+        }
+    }
+
     private fun onCloseClick() {
         when (val finalState = state) {
             is PreviewUrlUiState.Data -> delegate?.onPreviewUrlCloseClicked(finalState.eventId, finalState.url)
@@ -90,10 +103,11 @@ class PreviewUrlView @JvmOverloads constructor(
     // PRIVATE METHODS ****************************************************************************************************************************************
 
     private fun setupView() {
-        inflate(context, R.layout.url_preview, this)
-        views = UrlPreviewBinding.bind(this)
+        inflate(context, R.layout.view_url_preview, this)
+        views = ViewUrlPreviewBinding.bind(this)
 
         setOnClickListener(this)
+        views.urlPreviewImage.setOnClickListener { onImageClick() }
         views.urlPreviewClose.setOnClickListener { onCloseClick() }
     }
 
diff --git a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
index fded8602c4..43a3f748a5 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/DefaultNavigator.kt
@@ -73,7 +73,6 @@ import org.matrix.android.sdk.api.session.room.model.thirdparty.RoomDirectoryDat
 import org.matrix.android.sdk.api.session.terms.TermsService
 import org.matrix.android.sdk.api.session.widgets.model.Widget
 import org.matrix.android.sdk.api.session.widgets.model.WidgetType
-import org.matrix.android.sdk.api.util.MatrixItem
 import javax.inject.Inject
 import javax.inject.Singleton
 
@@ -256,14 +255,13 @@ class DefaultNavigator @Inject constructor(
         context.startActivity(RoomProfileActivity.newIntent(context, roomId, directAccess))
     }
 
-    override fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem) {
-        matrixItem.avatarUrl
+    override fun openBigImageViewer(activity: Activity, sharedElement: View?, mxcUrl: String?, title: String?) {
+        mxcUrl
                 ?.takeIf { it.isNotBlank() }
                 ?.let { avatarUrl ->
-                    val intent = BigImageViewerActivity.newIntent(activity, matrixItem.getBestName(), avatarUrl)
+                    val intent = BigImageViewerActivity.newIntent(activity, title, avatarUrl)
                     val options = sharedElement?.let {
-                        ActivityOptionsCompat.makeSceneTransitionAnimation(activity, it, ViewCompat.getTransitionName(it)
-                                ?: "")
+                        ActivityOptionsCompat.makeSceneTransitionAnimation(activity, it, ViewCompat.getTransitionName(it) ?: "")
                     }
                     activity.startActivity(intent, options?.toBundle())
                 }
diff --git a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
index 504fccb63a..dda071795b 100644
--- a/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
+++ b/vector/src/main/java/im/vector/app/features/navigation/Navigator.kt
@@ -80,7 +80,11 @@ interface Navigator {
 
     fun openRoomProfile(context: Context, roomId: String, directAccess: Int? = null)
 
-    fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem)
+    fun openBigImageViewer(activity: Activity, sharedElement: View?, matrixItem: MatrixItem) {
+        openBigImageViewer(activity, sharedElement, matrixItem.avatarUrl, matrixItem.getBestName())
+    }
+
+    fun openBigImageViewer(activity: Activity, sharedElement: View?, mxcUrl: String?, title: String?)
 
     fun openPinCode(context: Context,
                     activityResultLauncher: ActivityResultLauncher<Intent>,
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
index 58fd78e26a..0cb57fda4f 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
@@ -25,9 +25,7 @@ import android.view.MenuItem
 import android.view.View
 import android.view.ViewGroup
 import androidx.appcompat.app.AlertDialog
-import androidx.core.app.ActivityOptionsCompat
 import androidx.core.content.pm.ShortcutManagerCompat
-import androidx.core.view.ViewCompat
 import androidx.core.view.isVisible
 import com.airbnb.mvrx.args
 import com.airbnb.mvrx.fragmentViewModel
@@ -52,7 +50,6 @@ import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
 import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
 import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedAction
 import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
-import im.vector.app.features.media.BigImageViewerActivity
 import kotlinx.parcelize.Parcelize
 import org.matrix.android.sdk.api.session.room.notification.RoomNotificationState
 import org.matrix.android.sdk.api.util.MatrixItem
@@ -103,8 +100,8 @@ class RoomProfileFragment @Inject constructor(
         appBarStateChangeListener = MatrixItemAppBarStateChangeListener(
                 headerView,
                 listOf(views.matrixProfileToolbarAvatarImageView,
-                       views.matrixProfileToolbarTitleView,
-                       views.matrixProfileDecorationToolbarAvatarImageView)
+                        views.matrixProfileToolbarTitleView,
+                        views.matrixProfileDecorationToolbarAvatarImageView)
         )
         views.matrixProfileAppBarLayout.addOnOffsetChangedListener(appBarStateChangeListener)
         roomProfileViewModel.observeViewEvents {
@@ -289,13 +286,7 @@ class RoomProfileFragment @Inject constructor(
         )
     }
 
-    private fun onAvatarClicked(view: View, matrixItem: MatrixItem.RoomItem) = withState(roomProfileViewModel) {
-        matrixItem.avatarUrl
-                ?.takeIf { it.isNotEmpty() }
-                ?.let { avatarUrl ->
-                    val intent = BigImageViewerActivity.newIntent(requireContext(), matrixItem.getBestName(), avatarUrl)
-                    val options = ActivityOptionsCompat.makeSceneTransitionAnimation(requireActivity(), view, ViewCompat.getTransitionName(view) ?: "")
-                    startActivity(intent, options.toBundle())
-                }
+    private fun onAvatarClicked(view: View, matrixItem: MatrixItem.RoomItem) {
+        navigator.openBigImageViewer(requireActivity(), view, matrixItem)
     }
 }
diff --git a/vector/src/main/res/drawable-ldrtl/ic_arrow_right.xml b/vector/src/main/res/drawable-ldrtl/ic_arrow_right.xml
new file mode 100644
index 0000000000..b783231437
--- /dev/null
+++ b/vector/src/main/res/drawable-ldrtl/ic_arrow_right.xml
@@ -0,0 +1,16 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:width="7dp"
+    android:height="12dp"
+    android:viewportWidth="7"
+    android:viewportHeight="12">
+    <path
+        android:fillColor="#00000000"
+        android:fillType="evenOdd"
+        android:pathData="M6,11l-5,-5 5,-5"
+        android:strokeWidth="2"
+        android:strokeColor="#2E2F32"
+        android:strokeLineCap="round"
+        android:strokeLineJoin="round"
+        tools:strokeColor="#FFAF0F" />
+</vector>
diff --git a/vector/src/main/res/drawable/ic_arrow_right.xml b/vector/src/main/res/drawable/ic_arrow_right.xml
index c314196b66..76f2757723 100644
--- a/vector/src/main/res/drawable/ic_arrow_right.xml
+++ b/vector/src/main/res/drawable/ic_arrow_right.xml
@@ -1,14 +1,16 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:width="7dp"
     android:height="12dp"
     android:viewportWidth="7"
     android:viewportHeight="12">
-  <path
-      android:pathData="M1,11l5,-5 -5,-5"
-      android:strokeLineJoin="round"
-      android:strokeWidth="2"
-      android:fillColor="#00000000"
-      android:fillType="evenOdd"
-      android:strokeColor="#2E2F32"
-      android:strokeLineCap="round"/>
+    <path
+        android:fillColor="#00000000"
+        android:fillType="evenOdd"
+        android:pathData="M1,11l5,-5 -5,-5"
+        android:strokeWidth="2"
+        android:strokeColor="#2E2F32"
+        android:strokeLineCap="round"
+        android:strokeLineJoin="round"
+        tools:strokeColor="#FFAF0F" />
 </vector>
diff --git a/vector/src/main/res/layout/fragment_home_drawer.xml b/vector/src/main/res/layout/fragment_home_drawer.xml
index d56bae4678..e627882d96 100644
--- a/vector/src/main/res/layout/fragment_home_drawer.xml
+++ b/vector/src/main/res/layout/fragment_home_drawer.xml
@@ -84,6 +84,7 @@
             android:insetRight="0dp"
             android:insetBottom="0dp"
             android:padding="0dp"
+            android:rotationY="@integer/rtl_mirror_flip"
             app:cornerRadius="17dp"
             app:icon="@drawable/ic_qr_code_add"
             app:iconGravity="textStart"
diff --git a/vector/src/main/res/layout/url_preview.xml b/vector/src/main/res/layout/view_url_preview.xml
similarity index 100%
rename from vector/src/main/res/layout/url_preview.xml
rename to vector/src/main/res/layout/view_url_preview.xml