Merge pull request #1370 from vector-im/feature/redacted_message

Feature/redacted message
This commit is contained in:
Benoit Marty 2020-05-18 16:30:40 +02:00 committed by GitHub
commit e0c3f3638d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 168 additions and 27 deletions

View File

@ -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)

View File

@ -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
*/

View File

@ -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

View File

@ -16,8 +16,22 @@
package im.vector.matrix.android.internal.database.query
internal object FilterContent {
/**
* 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"*}"""
}
internal const val EDIT_TYPE = """{*"m.relates_to"*"rel_type":*"m.replace"*}"""
internal const val RESPONSE_TYPE = """{*"m.relates_to"*"rel_type":*"m.response"*}"""
/**
* To apply to Event.unsigned
*/
internal object Unsigned {
internal const val REDACTED = """{*"redacted_because":*}"""
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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()
}

View File

@ -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())

View File

@ -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
*

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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" />

View File

@ -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" />

View File

@ -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>

View File

@ -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"