Merge pull request #1370 from vector-im/feature/redacted_message
Feature/redacted message
This commit is contained in:
commit
e0c3f3638d
|
@ -6,6 +6,7 @@ Features ✨:
|
|||
|
||||
Improvements 🙌:
|
||||
- Better connectivity lost indicator when airplane mode is on
|
||||
- Add a setting to hide redacted events (#951)
|
||||
|
||||
Bugfix 🐛:
|
||||
- Fix issues with FontScale switch (#69, #645)
|
||||
|
|
|
@ -28,6 +28,10 @@ data class TimelineSettings(
|
|||
* A flag to filter edit events
|
||||
*/
|
||||
val filterEdits: Boolean = false,
|
||||
/**
|
||||
* A flag to filter redacted events
|
||||
*/
|
||||
val filterRedacted: Boolean = false,
|
||||
/**
|
||||
* A flag to filter by types. It should be used with [allowedTypes] field
|
||||
*/
|
||||
|
|
|
@ -62,8 +62,8 @@ internal fun TimelineEventEntity.Companion.latestEvent(realm: Realm,
|
|||
val liveEvents = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId)?.timelineEvents?.where()?.filterTypes(filterTypes)
|
||||
if (filterContentRelation) {
|
||||
liveEvents
|
||||
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.EDIT_TYPE)
|
||||
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.RESPONSE_TYPE)
|
||||
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
|
||||
?.not()?.like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
|
||||
}
|
||||
val query = if (includesSending && sendingTimelineEvents.findAll().isNotEmpty()) {
|
||||
sendingTimelineEvents
|
||||
|
|
|
@ -16,8 +16,22 @@
|
|||
|
||||
package im.vector.matrix.android.internal.database.query
|
||||
|
||||
internal object FilterContent {
|
||||
|
||||
internal const val EDIT_TYPE = """{*"m.relates_to"*"rel_type":*"m.replace"*}"""
|
||||
internal const val RESPONSE_TYPE = """{*"m.relates_to"*"rel_type":*"m.response"*}"""
|
||||
/**
|
||||
* Query strings used to filter the timeline events regarding the Json raw string of the Event
|
||||
*/
|
||||
internal object TimelineEventFilter {
|
||||
/**
|
||||
* To apply to Event.content
|
||||
*/
|
||||
internal object Content {
|
||||
internal const val EDIT = """{*"m.relates_to"*"rel_type":*"m.replace"*}"""
|
||||
internal const val RESPONSE = """{*"m.relates_to"*"rel_type":*"m.response"*}"""
|
||||
}
|
||||
|
||||
/**
|
||||
* To apply to Event.unsigned
|
||||
*/
|
||||
internal object Unsigned {
|
||||
internal const val REDACTED = """{*"redacted_because":*}"""
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryE
|
|||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.FilterContent
|
||||
import im.vector.matrix.android.internal.database.query.TimelineEventFilter
|
||||
import im.vector.matrix.android.internal.database.query.findAllInRoomWithSendStates
|
||||
import im.vector.matrix.android.internal.database.query.where
|
||||
import im.vector.matrix.android.internal.database.query.whereInRoom
|
||||
|
@ -724,8 +724,11 @@ internal class DefaultTimeline(
|
|||
`in`(TimelineEventEntityFields.ROOT.TYPE, settings.allowedTypes.toTypedArray())
|
||||
}
|
||||
if (settings.filterEdits) {
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.EDIT_TYPE)
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, FilterContent.RESPONSE_TYPE)
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.EDIT)
|
||||
not().like(TimelineEventEntityFields.ROOT.CONTENT, TimelineEventFilter.Content.RESPONSE)
|
||||
}
|
||||
if (settings.filterRedacted) {
|
||||
not().like(TimelineEventEntityFields.ROOT.UNSIGNED_DATA, TimelineEventFilter.Unsigned.REDACTED)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
@ -737,13 +740,19 @@ internal class DefaultTimeline(
|
|||
} else {
|
||||
true
|
||||
}
|
||||
if (!filterType) return@filter false
|
||||
|
||||
val filterEdits = if (settings.filterEdits && it.root.type == EventType.MESSAGE) {
|
||||
val messageContent = it.root.content.toModel<MessageContent>()
|
||||
messageContent?.relatesTo?.type != RelationType.REPLACE
|
||||
} else {
|
||||
true
|
||||
}
|
||||
filterType && filterEdits
|
||||
if (!filterEdits) return@filter false
|
||||
|
||||
val filterRedacted = settings.filterRedacted && it.root.isRedacted()
|
||||
|
||||
filterRedacted
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntit
|
|||
import im.vector.matrix.android.internal.database.model.ReadReceiptsSummaryEntityFields
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntity
|
||||
import im.vector.matrix.android.internal.database.model.TimelineEventEntityFields
|
||||
import im.vector.matrix.android.internal.database.query.FilterContent
|
||||
import im.vector.matrix.android.internal.database.query.TimelineEventFilter
|
||||
import im.vector.matrix.android.internal.database.query.whereInRoom
|
||||
import io.realm.OrderedRealmCollectionChangeListener
|
||||
import io.realm.Realm
|
||||
|
@ -149,16 +149,21 @@ internal class TimelineHiddenReadReceipts constructor(private val readReceiptsSu
|
|||
*/
|
||||
private fun RealmQuery<ReadReceiptsSummaryEntity>.filterReceiptsWithSettings(): RealmQuery<ReadReceiptsSummaryEntity> {
|
||||
beginGroup()
|
||||
var needOr = false
|
||||
if (settings.filterTypes) {
|
||||
not().`in`("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.TYPE}", settings.allowedTypes.toTypedArray())
|
||||
}
|
||||
if (settings.filterTypes && settings.filterEdits) {
|
||||
or()
|
||||
needOr = true
|
||||
}
|
||||
if (settings.filterEdits) {
|
||||
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", FilterContent.EDIT_TYPE)
|
||||
if (needOr) or()
|
||||
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.EDIT)
|
||||
or()
|
||||
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", FilterContent.RESPONSE_TYPE)
|
||||
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.CONTENT}", TimelineEventFilter.Content.RESPONSE)
|
||||
needOr = true
|
||||
}
|
||||
if (settings.filterRedacted) {
|
||||
if (needOr) or()
|
||||
like("${ReadReceiptsSummaryEntityFields.TIMELINE_EVENT}.${TimelineEventEntityFields.ROOT.UNSIGNED_DATA}", TimelineEventFilter.Unsigned.REDACTED)
|
||||
}
|
||||
endGroup()
|
||||
return this
|
||||
|
|
|
@ -29,6 +29,10 @@ class UserPreferencesProvider @Inject constructor(private val vectorPreferences:
|
|||
return vectorPreferences.showReadReceipts()
|
||||
}
|
||||
|
||||
fun shouldShowRedactedMessages(): Boolean {
|
||||
return vectorPreferences.showRedactedMessages()
|
||||
}
|
||||
|
||||
fun shouldShowLongClickOnRoomHelp(): Boolean {
|
||||
return vectorPreferences.shouldShowLongClickOnRoomHelp()
|
||||
}
|
||||
|
|
|
@ -99,11 +99,13 @@ class RoomDetailViewModel @AssistedInject constructor(
|
|||
private val timelineSettings = if (userPreferencesProvider.shouldShowHiddenEvents()) {
|
||||
TimelineSettings(30,
|
||||
filterEdits = false,
|
||||
filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(),
|
||||
filterTypes = false,
|
||||
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())
|
||||
} else {
|
||||
TimelineSettings(30,
|
||||
filterEdits = true,
|
||||
filterRedacted = userPreferencesProvider.shouldShowRedactedMessages().not(),
|
||||
filterTypes = true,
|
||||
allowedTypes = TimelineDisplayableEvents.DISPLAYABLE_TYPES,
|
||||
buildReadReceipts = userPreferencesProvider.shouldShowReadReceipts())
|
||||
|
|
|
@ -88,6 +88,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
private const val SETTINGS_ALWAYS_SHOW_TIMESTAMPS_KEY = "SETTINGS_ALWAYS_SHOW_TIMESTAMPS_KEY"
|
||||
private const val SETTINGS_12_24_TIMESTAMPS_KEY = "SETTINGS_12_24_TIMESTAMPS_KEY"
|
||||
private const val SETTINGS_SHOW_READ_RECEIPTS_KEY = "SETTINGS_SHOW_READ_RECEIPTS_KEY"
|
||||
private const val SETTINGS_SHOW_REDACTED_KEY = "SETTINGS_SHOW_REDACTED_KEY"
|
||||
private const val SETTINGS_SHOW_JOIN_LEAVE_MESSAGES_KEY = "SETTINGS_SHOW_JOIN_LEAVE_MESSAGES_KEY"
|
||||
private const val SETTINGS_SHOW_AVATAR_DISPLAY_NAME_CHANGES_MESSAGES_KEY = "SETTINGS_SHOW_AVATAR_DISPLAY_NAME_CHANGES_MESSAGES_KEY"
|
||||
private const val SETTINGS_VIBRATE_ON_MENTION_KEY = "SETTINGS_VIBRATE_ON_MENTION_KEY"
|
||||
|
@ -625,6 +626,15 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
|||
return defaultPrefs.getBoolean(SETTINGS_SHOW_READ_RECEIPTS_KEY, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the redacted message should be shown
|
||||
*
|
||||
* @return true if the redacted should be shown
|
||||
*/
|
||||
fun showRedactedMessages(): Boolean {
|
||||
return defaultPrefs.getBoolean(SETTINGS_SHOW_REDACTED_KEY, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if the help on room list should be shown
|
||||
*
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M1.5,3.6667H3.4444H14.5"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M11,3.6667L10,0.6667H6L5,3.6667"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M3.5,6.1667V13.8334C3.5,14.5697 4.097,15.1667 4.8333,15.1667H11.1667C11.903,15.1667 12.5,14.5697 12.5,13.8334V6.1667"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M6.5,6.1667V12.1667"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:strokeWidth="1"
|
||||
android:pathData="M9.5,6.1667V12.1667"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -0,0 +1,41 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M2.25,5.5H5.1667H21.75"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M16.5,5.5L15,1H9L7.5,5.5"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M5.25,9.25V20.75C5.25,21.8546 6.1454,22.75 7.25,22.75H16.75C17.8546,22.75 18.75,21.8546 18.75,20.75V9.25"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M9.75,9.25V18.25"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M14.25,9.25V18.25"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#2E2F32"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<corners android:radius="10dp" />
|
||||
<solid android:color="?vctr_redacted_message_color" />
|
||||
</shape>
|
|
@ -66,16 +66,15 @@
|
|||
android:id="@+id/decorationSpace"
|
||||
android:layout_width="4dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_toEndOf="@id/messageStartGuideline"
|
||||
/>
|
||||
android:layout_toEndOf="@id/messageStartGuideline" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/messageE2EDecoration"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_alignTop="@id/viewStubContainer"
|
||||
android:layout_marginTop="7dp"
|
||||
android:layout_alignEnd="@id/decorationSpace"
|
||||
android:layout_marginTop="7dp"
|
||||
android:visibility="gone"
|
||||
tools:src="@drawable/ic_shield_warning"
|
||||
tools:visibility="visible" />
|
||||
|
@ -119,7 +118,7 @@
|
|||
<ViewStub
|
||||
android:id="@+id/messageContentRedactedStub"
|
||||
style="@style/TimelineContentStubBaseParams"
|
||||
android:layout_height="20dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="56dp"
|
||||
android:layout="@layout/item_timeline_event_redacted_stub" />
|
||||
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:background="@drawable/redacted_background" />
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableStart="@drawable/ic_trash_16"
|
||||
android:drawablePadding="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:text="@string/event_redacted"
|
||||
android:textColor="?riotx_text_primary_body_contrast"
|
||||
android:textSize="14sp"
|
||||
app:drawableTint="?riotx_text_primary_body_contrast" />
|
|
@ -1563,6 +1563,9 @@ Why choose Riot.im?
|
|||
<string name="message_view_reaction">View Reactions</string>
|
||||
<string name="reactions">Reactions</string>
|
||||
|
||||
<string name="event_redacted">Message deleted</string>
|
||||
<string name="settings_show_redacted">Show removed messages</string>
|
||||
<string name="settings_show_redacted_summary">Show a placeholder for removed messages</string>
|
||||
<string name="event_redacted_by_user_reason">Event deleted by user</string>
|
||||
<string name="event_redacted_by_admin_reason">Event moderated by room admin</string>
|
||||
<string name="last_edited_info_message">Last edited by %1$s on %2$s</string>
|
||||
|
|
|
@ -70,6 +70,12 @@
|
|||
android:summary="@string/settings_show_read_receipts_summary"
|
||||
android:title="@string/settings_show_read_receipts" />
|
||||
|
||||
<im.vector.riotx.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="SETTINGS_SHOW_REDACTED_KEY"
|
||||
android:summary="@string/settings_show_redacted_summary"
|
||||
android:title="@string/settings_show_redacted" />
|
||||
|
||||
<im.vector.riotx.core.preference.VectorSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="SETTINGS_SHOW_JOIN_LEAVE_MESSAGES_KEY"
|
||||
|
|
Loading…
Reference in New Issue