Merge pull request #220 from vector-im/feature/restrict_reaction_emoji

Only show reactions with an emoji key
This commit is contained in:
Benoit Marty 2019-06-28 11:29:32 +02:00 committed by GitHub
commit 17a4a86ad1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 71 additions and 25 deletions

View File

@ -54,6 +54,7 @@ import im.vector.riotredesign.push.fcm.FcmHelper
import timber.log.Timber import timber.log.Timber
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import im.vector.riotredesign.core.utils.initKnownEmojiHashSet
import javax.inject.Inject import javax.inject.Inject


class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider { class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.Provider, androidx.work.Configuration.Provider {
@ -124,6 +125,8 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration.
FcmHelper.onEnterBackground(appContext, activeSessionHolder) FcmHelper.onEnterBackground(appContext, activeSessionHolder)
} }
}) })
//This should be done as early as possible
initKnownEmojiHashSet(appContext)
} }


override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION) override fun providesMatrixConfiguration() = MatrixConfiguration(BuildConfig.FLAVOR_DESCRIPTION)

View File

@ -1,5 +1,12 @@
package im.vector.riotredesign.core.utils package im.vector.riotredesign.core.utils


import android.content.Context
import com.squareup.moshi.Moshi
import im.vector.riotredesign.R
import im.vector.riotredesign.features.reactions.EmojiDataSource
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.regex.Pattern import java.util.regex.Pattern


private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" + private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" +
@ -26,6 +33,34 @@ private val emojisPattern = Pattern.compile("((?:[\uD83C\uDF00-\uD83D\uDDFF]" +
"|\uD83C\uDCCF\uFE0F?" + "|\uD83C\uDCCF\uFE0F?" +
"|[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?))") "|[\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA]\uFE0F?))")


//A hashset from all supported emoji
private var knownEmojiSet: HashSet<String>? = null

fun initKnownEmojiHashSet(context: Context, done: (() -> Unit)? = null) {
GlobalScope.launch {
context.resources.openRawResource(R.raw.emoji_picker_datasource).use { input ->
val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(EmojiDataSource.EmojiData::class.java)
val inputAsString = input.bufferedReader().use { it.readText() }
val source = jsonAdapter.fromJson(inputAsString)
knownEmojiSet = HashSet<String>()
source?.emojis?.values?.forEach {
knownEmojiSet?.add(it.emojiString())
}
done?.invoke()
}
}
}

fun isSingleEmoji(string: String): Boolean {
if (knownEmojiSet == null) {
Timber.e("Known Emoji Hashset not initialized")
//use fallback regexp
return containsOnlyEmojis(string)
}
return knownEmojiSet?.contains(string) ?: false
}

/** /**
* Test if a string contains emojis. * Test if a string contains emojis.
* It seems that the regex [emoji_regex]+ does not work. * It seems that the regex [emoji_regex]+ does not work.
@ -66,4 +101,3 @@ fun containsOnlyEmojis(str: String?): Boolean {


return res return res
} }


View File

@ -36,6 +36,9 @@ import im.vector.riotredesign.core.resources.StringProvider
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
import org.json.JSONObject import org.json.JSONObject


import im.vector.riotredesign.core.utils.isSingleEmoji


data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null) data class SimpleAction(val uid: String, val titleRes: Int, val iconResId: Int?, val data: Any? = null)


data class MessageMenuState( data class MessageMenuState(
@ -92,7 +95,7 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state val event = session.getRoom(state.roomId)?.getTimeLineEvent(state.eventId) ?: return state


val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel() val messageContent: MessageContent? = event.annotations?.editSummary?.aggregatedContent?.toModel()
?: event.root.content.toModel() ?: event.root.content.toModel()
val type = messageContent?.type val type = messageContent?.type


val actions = if (!event.sendState.isSent()) { val actions = if (!event.sendState.isSent()) {
@ -143,8 +146,8 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
if (messageContent is MessageImageContent) { if (messageContent is MessageImageContent) {
this.add( this.add(
SimpleAction(ACTION_SHARE, SimpleAction(ACTION_SHARE,
R.string.share, R.drawable.ic_share, R.string.share, R.drawable.ic_share,
session.contentUrlResolver().resolveFullSize(messageContent.url)) session.contentUrlResolver().resolveFullSize(messageContent.url))
) )
} }
//TODO //TODO
@ -212,31 +215,31 @@ class MessageMenuViewModel @AssistedInject constructor(@Assisted initialState: M
} }
} }


private fun canRedact(event: TimelineEvent, myUserId: String): Boolean { private fun canRedact(event: TimelineEvent, myUserId: String): Boolean {
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
if (event.root.getClearType() != EventType.MESSAGE) return false if (event.root.getClearType() != EventType.MESSAGE) return false
//TODO if user is admin or moderator //TODO if user is admin or moderator
return event.root.senderId == myUserId return event.root.senderId == myUserId
} }


private fun canViewReactions(event: TimelineEvent): Boolean { private fun canViewReactions(event: TimelineEvent): Boolean {
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
if (event.root.getClearType() != EventType.MESSAGE) return false if (event.root.getClearType() != EventType.MESSAGE) return false
//TODO if user is admin or moderator //TODO if user is admin or moderator
return event.annotations?.reactionsSummary?.isNotEmpty() ?: false return event.annotations?.reactionsSummary?.any { isSingleEmoji(it.key) } ?: false
} }




private fun canEdit(event: TimelineEvent, myUserId: String): Boolean { private fun canEdit(event: TimelineEvent, myUserId: String): Boolean {
//Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment //Only event of type Event.EVENT_TYPE_MESSAGE are supported for the moment
if (event.root.getClearType() != EventType.MESSAGE) return false if (event.root.getClearType() != EventType.MESSAGE) return false
//TODO if user is admin or moderator //TODO if user is admin or moderator
val messageContent = event.root.content.toModel<MessageContent>() val messageContent = event.root.content.toModel<MessageContent>()
return event.root.senderId == myUserId && ( return event.root.senderId == myUserId && (
messageContent?.type == MessageType.MSGTYPE_TEXT messageContent?.type == MessageType.MSGTYPE_TEXT
|| messageContent?.type == MessageType.MSGTYPE_EMOTE || messageContent?.type == MessageType.MSGTYPE_EMOTE
) )
} }




private fun canCopy(type: String?): Boolean { private fun canCopy(type: String?): Boolean {

View File

@ -8,6 +8,7 @@ import im.vector.matrix.android.api.session.room.model.ReactionAggregatedSummary
import im.vector.matrix.rx.RxRoom import im.vector.matrix.rx.RxRoom
import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.platform.VectorViewModel import im.vector.riotredesign.core.platform.VectorViewModel
import im.vector.riotredesign.core.utils.isSingleEmoji
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@ -69,7 +70,9 @@ class ViewReactionViewModel @AssistedInject constructor(@Assisted
.flatMapSingle { summaries -> .flatMapSingle { summaries ->
Observable Observable
.fromIterable(summaries) .fromIterable(summaries)
.flatMapIterable {it.reactionsSummary} .flatMapIterable { it.reactionsSummary
.filter { reactionAggregatedSummary -> isSingleEmoji(reactionAggregatedSummary.key) }
}
.toReactionInfoList() .toReactionInfoList()
} }
.execute { .execute {

View File

@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.core.resources.ColorProvider import im.vector.riotredesign.core.resources.ColorProvider
import im.vector.riotredesign.core.utils.isSingleEmoji
import im.vector.riotredesign.features.home.getColorFromUserId import im.vector.riotredesign.features.home.getColorFromUserId
import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter import im.vector.riotredesign.features.home.room.detail.timeline.helper.TimelineDateFormatter
import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData import im.vector.riotredesign.features.home.room.detail.timeline.item.MessageInformationData
@ -68,9 +69,11 @@ class MessageInformationDataFactory @Inject constructor(private val timelineDate
avatarUrl = avatarUrl, avatarUrl = avatarUrl,
memberName = formattedMemberName, memberName = formattedMemberName,
showInformation = showInformation, showInformation = showInformation,
orderedReactionList = event.annotations?.reactionsSummary?.map { orderedReactionList = event.annotations?.reactionsSummary
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty()) ?.filter { isSingleEmoji(it.key) }
}, ?.map {
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
},
hasBeenEdited = hasBeenEdited hasBeenEdited = hasBeenEdited
) )
} }