PR Review

This commit is contained in:
Benoit Marty 2020-12-15 10:21:12 +01:00
parent 7da8b13cde
commit 6957768567
4 changed files with 100 additions and 112 deletions

View File

@ -35,7 +35,7 @@ enum class ChatEffect {
* It also manages effect duration and some cool down, for example if an effect is currently playing, * It also manages effect duration and some cool down, for example if an effect is currently playing,
* any other trigger will be ignored * any other trigger will be ignored
* For now it uses visibility callback to check for an effect (that means that a fail to decrypt event - more * For now it uses visibility callback to check for an effect (that means that a fail to decrypt event - more
* precisly an event decrypted with a few delay won't trigger an effect; it's acceptable) * precisely an event decrypted with a few delay won't trigger an effect; it's acceptable)
* Events that are more that 10s old won't trigger effects * Events that are more that 10s old won't trigger effects
*/ */
class ChatEffectManager @Inject constructor() { class ChatEffectManager @Inject constructor() {
@ -50,7 +50,7 @@ class ChatEffectManager @Inject constructor() {
private var stopTimer: Timer? = null private var stopTimer: Timer? = null
// an in memory store to avoid trigger twice for an event (quick close/open timeline) // an in memory store to avoid trigger twice for an event (quick close/open timeline)
private val alreadyPlayed = emptyList<String>().toMutableList() private val alreadyPlayed = mutableListOf<String>()
fun checkForEffect(event: TimelineEvent) { fun checkForEffect(event: TimelineEvent) {
val age = event.root.ageLocalTs ?: 0 val age = event.root.ageLocalTs ?: 0
@ -80,7 +80,6 @@ class ChatEffectManager @Inject constructor() {
fun dispose() { fun dispose() {
stopTimer?.cancel() stopTimer?.cancel()
stopTimer = null stopTimer = null
delegate = null
alreadyPlayed.clear() alreadyPlayed.clear()
} }
@ -98,7 +97,7 @@ class ChatEffectManager @Inject constructor() {
} }
} }
private fun hasAlreadyPlayed(event: TimelineEvent) : Boolean { private fun hasAlreadyPlayed(event: TimelineEvent): Boolean {
return alreadyPlayed.contains(event.eventId) return alreadyPlayed.contains(event.eventId)
|| (event.root.unsignedData?.transactionId?.let { alreadyPlayed.contains(it) } ?: false) || (event.root.unsignedData?.transactionId?.let { alreadyPlayed.contains(it) } ?: false)
} }
@ -106,20 +105,30 @@ class ChatEffectManager @Inject constructor() {
private fun findEffect(content: MessageContent, event: TimelineEvent): ChatEffect? { private fun findEffect(content: MessageContent, event: TimelineEvent): ChatEffect? {
return when (content.msgType) { return when (content.msgType) {
MessageType.MSGTYPE_CONFETTI -> ChatEffect.CONFETTI MessageType.MSGTYPE_CONFETTI -> ChatEffect.CONFETTI
MessageType.MSGTYPE_SNOW -> ChatEffect.SNOW MessageType.MSGTYPE_SNOW -> ChatEffect.SNOW
MessageType.MSGTYPE_TEXT -> {
MessageType.MSGTYPE_TEXT -> { event.root.getClearContent().toModel<MessageContent>()?.body
val text = event.root.getClearContent().toModel<MessageContent>()?.body ?: "" ?.let { text ->
if (text.contains("🎉") when {
|| text.contains("🎊")) { EMOJIS_FOR_CONFETTI.any { text.contains(it) } -> ChatEffect.CONFETTI
ChatEffect.CONFETTI EMOJIS_FOR_SNOW.any { text.contains(it) } -> ChatEffect.SNOW
} else if (text.contains("⛄️") else -> null
|| text.contains("☃️") }
|| text.contains("❄️")) { }
ChatEffect.SNOW
} else null
} }
else -> null else -> null
} }
} }
companion object {
private val EMOJIS_FOR_CONFETTI = listOf(
"🎉",
"🎊"
)
private val EMOJIS_FOR_SNOW = listOf(
"⛄️",
"☃️",
"❄️"
)
}
} }

View File

@ -226,58 +226,58 @@ class RoomDetailViewModel @AssistedInject constructor(
override fun handle(action: RoomDetailAction) { override fun handle(action: RoomDetailAction) {
when (action) { when (action) {
is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action) is RoomDetailAction.UserIsTyping -> handleUserIsTyping(action)
is RoomDetailAction.SaveDraft -> handleSaveDraft(action) is RoomDetailAction.SaveDraft -> handleSaveDraft(action)
is RoomDetailAction.SendMessage -> handleSendMessage(action) is RoomDetailAction.SendMessage -> handleSendMessage(action)
is RoomDetailAction.SendMedia -> handleSendMedia(action) is RoomDetailAction.SendMedia -> handleSendMedia(action)
is RoomDetailAction.SendSticker -> handleSendSticker(action) is RoomDetailAction.SendSticker -> handleSendSticker(action)
is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action) is RoomDetailAction.TimelineEventTurnsVisible -> handleEventVisible(action)
is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action) is RoomDetailAction.TimelineEventTurnsInvisible -> handleEventInvisible(action)
is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action) is RoomDetailAction.LoadMoreTimelineEvents -> handleLoadMore(action)
is RoomDetailAction.SendReaction -> handleSendReaction(action) is RoomDetailAction.SendReaction -> handleSendReaction(action)
is RoomDetailAction.AcceptInvite -> handleAcceptInvite() is RoomDetailAction.AcceptInvite -> handleAcceptInvite()
is RoomDetailAction.RejectInvite -> handleRejectInvite() is RoomDetailAction.RejectInvite -> handleRejectInvite()
is RoomDetailAction.RedactAction -> handleRedactEvent(action) is RoomDetailAction.RedactAction -> handleRedactEvent(action)
is RoomDetailAction.UndoReaction -> handleUndoReact(action) is RoomDetailAction.UndoReaction -> handleUndoReact(action)
is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action) is RoomDetailAction.UpdateQuickReactAction -> handleUpdateQuickReaction(action)
is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action) is RoomDetailAction.EnterRegularMode -> handleEnterRegularMode(action)
is RoomDetailAction.EnterEditMode -> handleEditAction(action) is RoomDetailAction.EnterEditMode -> handleEditAction(action)
is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action) is RoomDetailAction.EnterQuoteMode -> handleQuoteAction(action)
is RoomDetailAction.EnterReplyMode -> handleReplyAction(action) is RoomDetailAction.EnterReplyMode -> handleReplyAction(action)
is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action) is RoomDetailAction.DownloadOrOpen -> handleOpenOrDownloadFile(action)
is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action) is RoomDetailAction.NavigateToEvent -> handleNavigateToEvent(action)
is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action) is RoomDetailAction.HandleTombstoneEvent -> handleTombstoneEvent(action)
is RoomDetailAction.ResendMessage -> handleResendEvent(action) is RoomDetailAction.ResendMessage -> handleResendEvent(action)
is RoomDetailAction.RemoveFailedEcho -> handleRemove(action) is RoomDetailAction.RemoveFailedEcho -> handleRemove(action)
is RoomDetailAction.ResendAll -> handleResendAll() is RoomDetailAction.ResendAll -> handleResendAll()
is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead() is RoomDetailAction.MarkAllAsRead -> handleMarkAllAsRead()
is RoomDetailAction.ReportContent -> handleReportContent(action) is RoomDetailAction.ReportContent -> handleReportContent(action)
is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action) is RoomDetailAction.IgnoreUser -> handleIgnoreUser(action)
is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages() is RoomDetailAction.EnterTrackingUnreadMessagesState -> startTrackingUnreadMessages()
is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages() is RoomDetailAction.ExitTrackingUnreadMessagesState -> stopTrackingUnreadMessages()
is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action) is RoomDetailAction.ReplyToOptions -> handleReplyToOptions(action)
is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action) is RoomDetailAction.AcceptVerificationRequest -> handleAcceptVerification(action)
is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action) is RoomDetailAction.DeclineVerificationRequest -> handleDeclineVerification(action)
is RoomDetailAction.RequestVerification -> handleRequestVerification(action) is RoomDetailAction.RequestVerification -> handleRequestVerification(action)
is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action) is RoomDetailAction.ResumeVerification -> handleResumeRequestVerification(action)
is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action) is RoomDetailAction.ReRequestKeys -> handleReRequestKeys(action)
is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action) is RoomDetailAction.TapOnFailedToDecrypt -> handleTapOnFailedToDecrypt(action)
is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment() is RoomDetailAction.SelectStickerAttachment -> handleSelectStickerAttachment()
is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager() is RoomDetailAction.OpenIntegrationManager -> handleOpenIntegrationManager()
is RoomDetailAction.StartCall -> handleStartCall(action) is RoomDetailAction.StartCall -> handleStartCall(action)
is RoomDetailAction.EndCall -> handleEndCall() is RoomDetailAction.EndCall -> handleEndCall()
is RoomDetailAction.ManageIntegrations -> handleManageIntegrations() is RoomDetailAction.ManageIntegrations -> handleManageIntegrations()
is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action) is RoomDetailAction.AddJitsiWidget -> handleAddJitsiConference(action)
is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId)
is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action)
is RoomDetailAction.CancelSend -> handleCancel(action) is RoomDetailAction.CancelSend -> handleCancel(action)
is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action) is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action)
is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action)
RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople() RoomDetailAction.QuickActionInvitePeople -> handleInvitePeople()
RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar() RoomDetailAction.QuickActionSetAvatar -> handleQuickSetAvatar()
is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action) is RoomDetailAction.SetAvatarAction -> handleSetNewAvatar(action)
RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings) RoomDetailAction.QuickActionSetTopic -> _viewEvents.post(RoomDetailViewEvents.OpenRoomSettings)
is RoomDetailAction.ShowRoomAvatarFullScreen -> { is RoomDetailAction.ShowRoomAvatarFullScreen -> {
_viewEvents.post( _viewEvents.post(
RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView) RoomDetailViewEvents.ShowRoomAvatarFullScreen(action.matrixItem, action.transitionView)
) )
@ -593,15 +593,15 @@ class RoomDetailViewModel @AssistedInject constructor(
return@withState false return@withState false
} }
when (itemId) { when (itemId) {
R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true R.id.resend_all -> state.asyncRoomSummary()?.hasFailedSending == true
R.id.timeline_setting -> true R.id.timeline_setting -> true
R.id.invite -> state.canInvite R.id.invite -> state.canInvite
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
R.id.open_matrix_apps -> true R.id.open_matrix_apps -> true
R.id.voice_call, R.id.voice_call,
R.id.video_call -> true // always show for discoverability R.id.video_call -> true // always show for discoverability
R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null R.id.hangup_call -> webRtcPeerConnectionManager.currentCall != null
R.id.search -> true R.id.search -> true
else -> false else -> false
} }
} }
@ -748,7 +748,7 @@ class RoomDetailViewModel @AssistedInject constructor(
} }
}.exhaustive }.exhaustive
} }
is SendMode.EDIT -> { is SendMode.EDIT -> {
// is original event a reply? // is original event a reply?
val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId val inReplyTo = state.sendMode.timelineEvent.root.getClearContent().toModel<MessageContent>()?.relatesTo?.inReplyTo?.eventId
?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId ?: state.sendMode.timelineEvent.root.content.toModel<EncryptedEventContent>()?.relatesTo?.inReplyTo?.eventId
@ -774,7 +774,7 @@ class RoomDetailViewModel @AssistedInject constructor(
_viewEvents.post(RoomDetailViewEvents.MessageSent) _viewEvents.post(RoomDetailViewEvents.MessageSent)
popDraft() popDraft()
} }
is SendMode.QUOTE -> { is SendMode.QUOTE -> {
val messageContent: MessageContent? = val messageContent: MessageContent? =
state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel() state.sendMode.timelineEvent.annotations?.editSummary?.aggregatedContent.toModel()
?: state.sendMode.timelineEvent.root.getClearContent().toModel() ?: state.sendMode.timelineEvent.root.getClearContent().toModel()
@ -797,7 +797,7 @@ class RoomDetailViewModel @AssistedInject constructor(
_viewEvents.post(RoomDetailViewEvents.MessageSent) _viewEvents.post(RoomDetailViewEvents.MessageSent)
popDraft() popDraft()
} }
is SendMode.REPLY -> { is SendMode.REPLY -> {
state.sendMode.timelineEvent.let { state.sendMode.timelineEvent.let {
room.replyToMessage(it, action.text.toString(), action.autoMarkdown) room.replyToMessage(it, action.text.toString(), action.autoMarkdown)
_viewEvents.post(RoomDetailViewEvents.MessageSent) _viewEvents.post(RoomDetailViewEvents.MessageSent)
@ -998,14 +998,7 @@ class RoomDetailViewModel @AssistedInject constructor(
} }
override fun shouldStartEffect(effect: ChatEffect) { override fun shouldStartEffect(effect: ChatEffect) {
when (effect) { _viewEvents.post(RoomDetailViewEvents.StartChatEffect(effect))
ChatEffect.CONFETTI -> {
_viewEvents.post(RoomDetailViewEvents.StartChatEffect(ChatEffect.CONFETTI))
}
ChatEffect.SNOW -> {
_viewEvents.post(RoomDetailViewEvents.StartChatEffect(ChatEffect.SNOW))
}
}
} }
override fun stopEffects() { override fun stopEffects() {
@ -1413,6 +1406,7 @@ class RoomDetailViewModel @AssistedInject constructor(
if (vectorPreferences.sendTypingNotifs()) { if (vectorPreferences.sendTypingNotifs()) {
room.userStopsTyping() room.userStopsTyping()
} }
chatEffectManager.delegate = null
chatEffectManager.dispose() chatEffectManager.dispose()
super.onCleared() super.onCleared()
} }

View File

@ -1,13 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="106dp"
android:height="106dp"
android:viewportWidth="106"
android:viewportHeight="106">
<path
android:pathData="M53,53m-52,0a52,52 0,1 1,104 0a52,52 0,1 1,-104 0"
android:strokeAlpha="0.5168103"
android:strokeWidth="1"
android:fillColor="#FFFFFF"
android:strokeColor="#979797"
android:fillType="evenOdd"/>
</vector>

View File

@ -6,21 +6,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<nl.dionsegijn.konfetti.KonfettiView
android:id="@+id/viewKonfetti"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp"
android:visibility="invisible" />
<com.jetradarmobile.snowfall.SnowfallView
android:id="@+id/viewSnowFall"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?vctr_chat_effect_snow_background"
android:elevation="4dp"
android:visibility="invisible" />
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/roomToolbar" android:id="@+id/roomToolbar"
style="@style/VectorToolbarStyle" style="@style/VectorToolbarStyle"
@ -240,4 +225,17 @@
app:maxImageSize="16dp" app:maxImageSize="16dp"
app:tint="@color/black" /> app:tint="@color/black" />
<nl.dionsegijn.konfetti.KonfettiView
android:id="@+id/viewKonfetti"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<com.jetradarmobile.snowfall.SnowfallView
android:id="@+id/viewSnowFall"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?vctr_chat_effect_snow_background"
android:visibility="invisible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>