Code review fixes.

This commit is contained in:
Onuray Sahin 2021-12-10 17:57:57 +03:00
parent be9e592aa5
commit 9b2a3cf445
11 changed files with 158 additions and 83 deletions

View file

@ -18,13 +18,12 @@ package org.matrix.android.sdk.api.session.room.model.message
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
/**
* Class representing the org.matrix.msc3381.poll.end event content
*/
@JsonClass(generateAdapter = true)
data class MessageEndPollContent(
@Json(name = "rel_type") val relationType: String = RelationType.REFERENCE,
@Json(name = "event_id") val eventId: String
@Json(name = "m.relates_to") val relatesTo: RelationDefaultContent? = null
)

View file

@ -406,7 +406,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor(
content: MessageEndPollContent,
roomId: String,
isLocalEcho: Boolean) {
val pollEventId = content.eventId
val pollEventId = content.relatesTo?.eventId ?: return
var existing = EventAnnotationsSummaryEntity.where(realm, roomId, pollEventId).findFirst()
if (existing == null) {

View file

@ -178,7 +178,10 @@ internal class LocalEchoEventFactory @Inject constructor(
fun createEndPollEvent(roomId: String,
eventId: String): Event {
val content = MessageEndPollContent(
eventId = eventId
relatesTo = RelationDefaultContent(
type = RelationType.REFERENCE,
eventId = eventId
)
)
val localId = LocalEcho.createLocalEchoId()
return Event(
@ -440,7 +443,7 @@ internal class LocalEchoEventFactory @Inject constructor(
when (content?.msgType) {
MessageType.MSGTYPE_EMOTE,
MessageType.MSGTYPE_TEXT,
MessageType.MSGTYPE_NOTICE -> {
MessageType.MSGTYPE_NOTICE -> {
var formattedText: String? = null
if (content is MessageContentWithFormattedBody) {
formattedText = content.matrixFormattedBody
@ -451,11 +454,12 @@ internal class LocalEchoEventFactory @Inject constructor(
TextContent(content.body, formattedText)
}
}
MessageType.MSGTYPE_FILE -> return TextContent("sent a file.")
MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.")
MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.")
MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.")
else -> return TextContent(content?.body ?: "")
MessageType.MSGTYPE_FILE -> return TextContent("sent a file.")
MessageType.MSGTYPE_AUDIO -> return TextContent("sent an audio file.")
MessageType.MSGTYPE_IMAGE -> return TextContent("sent an image.")
MessageType.MSGTYPE_VIDEO -> return TextContent("sent a video.")
MessageType.MSGTYPE_POLL_START -> return TextContent((content as? MessagePollContent)?.pollCreationInfo?.question?.question ?: "")
else -> return TextContent(content?.body ?: "")
}
}

View file

@ -203,6 +203,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
import org.matrix.android.sdk.api.session.room.model.message.MessageImageInfoContent
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
import org.matrix.android.sdk.api.session.room.model.message.MessageStickerContent
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
@ -1077,6 +1078,8 @@ class RoomDetailFragment @Inject constructor(
val nonFormattedBody = if (messageContent is MessageAudioContent && messageContent.voiceMessageIndicator != null) {
val formattedDuration = DateUtils.formatElapsedTime(((messageContent.audioInfo?.duration ?: 0) / 1000).toLong())
getString(R.string.voice_message_reply_content, formattedDuration)
} else if (messageContent is MessagePollContent) {
messageContent.pollCreationInfo?.question?.question
} else {
messageContent?.body ?: ""
}

View file

@ -399,8 +399,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
}
private fun canReply(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean {
// Only event of type EventType.MESSAGE are supported for the moment
if (event.root.getClearType() != EventType.MESSAGE) return false
// Only EventType.MESSAGE and EventType.POLL_START event types are supported for the moment
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.POLL_START)) return false
if (!actionPermissions.canSendMessage) return false
return when (messageContent?.msgType) {
MessageType.MSGTYPE_TEXT,
@ -409,8 +409,9 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
MessageType.MSGTYPE_IMAGE,
MessageType.MSGTYPE_VIDEO,
MessageType.MSGTYPE_AUDIO,
MessageType.MSGTYPE_FILE -> true
else -> false
MessageType.MSGTYPE_FILE,
MessageType.MSGTYPE_POLL_START -> true
else -> false
}
}

View file

@ -179,6 +179,7 @@ class MessageItemFactory @Inject constructor(
.attributes(attributes)
.eventId(informationData.eventId)
.pollResponseSummary(informationData.pollResponseAggregatedSummary)
.pollSent(informationData.sendState.isSent())
.pollContent(messageContent)
.highlighted(highlight)
.leftGuideline(avatarSizeProvider.leftGuideline)

View file

@ -1,17 +0,0 @@
/*
* Copyright 2019 New Vector Ltd
*
* 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 im.vector.app.features.home.room.detail.timeline.item

View file

@ -41,6 +41,9 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
@EpoxyAttribute
var eventId: String? = null
@EpoxyAttribute
var pollSent: Boolean = false
override fun bind(holder: Holder) {
super.bind(holder)
val relatedEventId = eventId ?: return
@ -53,7 +56,6 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
val isEnded = pollResponseSummary?.isClosed.orFalse()
val didUserVoted = pollResponseSummary?.myVote?.isNotEmpty().orFalse()
val showVotes = didUserVoted || isEnded
val totalVotes = pollResponseSummary?.totalVotes ?: 0
val winnerVoteCount = pollResponseSummary?.winnerVoteCount
@ -62,21 +64,30 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
val isMyVote = pollResponseSummary?.myVote == option.id
val voteCount = voteSummary?.total ?: 0
val votePercentage = voteSummary?.percentage ?: 0.0
val optionName = option.answer ?: ""
holder.optionsContainer.addView(
PollOptionItem(holder.view.context).apply {
update(optionName = option.answer ?: "",
isSelected = isMyVote,
isWinner = voteCount == winnerVoteCount,
isEnded = isEnded,
showVote = showVotes,
voteCount = voteCount,
votePercentage = votePercentage,
callback = object : PollOptionItem.Callback {
override fun onOptionClicked() {
callback?.onTimelineItemAction(RoomDetailAction.VoteToPoll(relatedEventId, option.id ?: ""))
}
})
val callback = object : PollOptionItem.Callback {
override fun onOptionClicked() {
callback?.onTimelineItemAction(RoomDetailAction.VoteToPoll(relatedEventId, option.id ?: ""))
}
}
if (!pollSent) {
// Poll event is not send yet. Disable option.
render(PollOptionViewState.DisabledOptionWithInvisibleVotes(optionName), callback)
} else if (isEnded) {
// Poll is ended. Disable option, show votes and mark the winner.
val isWinner = winnerVoteCount != 0 && voteCount == winnerVoteCount
render(PollOptionViewState.DisabledOptionWithVisibleVotes(optionName, voteCount, votePercentage, isWinner), callback)
} else if (didUserVoted) {
// User voted to the poll, but poll is not ended. Enable option, show votes and mark the user's selection.
render(PollOptionViewState.EnabledOptionWithVisibleVotes(optionName, voteCount, votePercentage, isMyVote), callback)
} else {
// User didn't voted yet and poll is not ended yet. Enable options, hide votes.
render(PollOptionViewState.EnabledOptionWithInvisibleVotes(optionName), callback)
}
}
)
}

View file

@ -50,51 +50,76 @@ class PollOptionItem @JvmOverloads constructor(
views.root.setOnClickListener { callback?.onOptionClicked() }
}
fun update(optionName: String,
isSelected: Boolean,
isWinner: Boolean,
isEnded: Boolean,
showVote: Boolean,
voteCount: Int,
votePercentage: Double,
callback: Callback) {
fun render(state: PollOptionViewState, callback: Callback) {
this.callback = callback
views.optionNameTextView.text = optionName
views.optionCheckImageView.isVisible = !isEnded
views.optionNameTextView.text = state.name
if (isEnded && isWinner) {
views.optionBorderImageView.setAttributeTintedImageResource(R.drawable.bg_poll_option, R.attr.colorPrimary)
views.optionVoteProgress.progressDrawable = AppCompatResources.getDrawable(context, R.drawable.poll_option_progressbar_checked)
views.optionWinnerImageView.isVisible = true
} else if (isSelected) {
when (state) {
is PollOptionViewState.DisabledOptionWithInvisibleVotes -> renderDisabledOptionWithInvisibleVotes()
is PollOptionViewState.DisabledOptionWithVisibleVotes -> renderDisabledOptionWithVisibleVotes(state)
is PollOptionViewState.EnabledOptionWithInvisibleVotes -> renderEnabledOptionWithInvisibleVotes()
is PollOptionViewState.EnabledOptionWithVisibleVotes -> renderEnabledOptionWithVisibleVotes(state)
}
}
private fun renderDisabledOptionWithInvisibleVotes() {
views.optionCheckImageView.isVisible = false
views.optionWinnerImageView.isVisible = false
hideVotes()
renderVoteSelection(false)
}
private fun renderDisabledOptionWithVisibleVotes(state: PollOptionViewState.DisabledOptionWithVisibleVotes) {
views.optionCheckImageView.isVisible = false
views.optionWinnerImageView.isVisible = state.isWinner
showVotes(state.voteCount, state.votePercentage)
renderVoteSelection(state.isWinner)
}
private fun renderEnabledOptionWithInvisibleVotes() {
views.optionCheckImageView.isVisible = true
views.optionWinnerImageView.isVisible = false
hideVotes()
renderVoteSelection(false)
}
private fun renderEnabledOptionWithVisibleVotes(state: PollOptionViewState.EnabledOptionWithVisibleVotes) {
views.optionCheckImageView.isVisible = true
views.optionWinnerImageView.isVisible = false
showVotes(state.voteCount, state.votePercentage)
renderVoteSelection(state.isSelected)
}
private fun showVotes(voteCount: Int, votePercentage: Double) {
views.optionVoteCountTextView.apply {
isVisible = true
text = resources.getQuantityString(R.plurals.poll_option_vote_count, voteCount, voteCount)
}
views.optionVoteProgress.apply {
val progressValue = (votePercentage * 100).toInt()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setProgress(progressValue, true)
} else {
progress = progressValue
}
}
}
private fun hideVotes() {
views.optionVoteCountTextView.isVisible = false
views.optionVoteProgress.progress = 0
}
private fun renderVoteSelection(isSelected: Boolean) {
if (isSelected) {
views.optionBorderImageView.setAttributeTintedImageResource(R.drawable.bg_poll_option, R.attr.colorPrimary)
views.optionVoteProgress.progressDrawable = AppCompatResources.getDrawable(context, R.drawable.poll_option_progressbar_checked)
views.optionCheckImageView.setImageResource(R.drawable.poll_option_checked)
views.optionWinnerImageView.isVisible = false
} else {
views.optionBorderImageView.setAttributeTintedImageResource(R.drawable.bg_poll_option, R.attr.vctr_content_quinary)
views.optionVoteProgress.progressDrawable = AppCompatResources.getDrawable(context, R.drawable.poll_option_progressbar_unchecked)
views.optionCheckImageView.setImageResource(R.drawable.poll_option_unchecked)
views.optionWinnerImageView.isVisible = false
}
if (showVote) {
views.optionVoteCountTextView.apply {
isVisible = true
text = resources.getQuantityString(R.plurals.poll_option_vote_count, voteCount, voteCount)
}
views.optionVoteProgress.apply {
val progressValue = (votePercentage * 100).toInt()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setProgress(progressValue, true)
} else {
progress = progressValue
}
}
} else {
views.optionVoteCountTextView.isVisible = false
views.optionVoteProgress.progress = 0
}
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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 im.vector.app.features.home.room.detail.timeline.item
sealed class PollOptionViewState(open val name: String) {
/**
* Represents a poll that user already voted.
*/
data class EnabledOptionWithVisibleVotes(override val name: String,
val voteCount: Int,
val votePercentage: Double,
val isSelected: Boolean
) : PollOptionViewState(name)
/**
* Represents a poll that is ended.
*/
data class DisabledOptionWithVisibleVotes(override val name: String,
val voteCount: Int,
val votePercentage: Double,
val isWinner: Boolean
) : PollOptionViewState(name)
/**
* Represents a poll that is sent but not voted by the user
*/
data class EnabledOptionWithInvisibleVotes(override val name: String) : PollOptionViewState(name)
/**
* Represents a poll that is not sent to the server yet.
*/
data class DisabledOptionWithInvisibleVotes(override val name: String) : PollOptionViewState(name)
}

View file

@ -7,10 +7,10 @@
<TextView
android:id="@+id/questionTextView"
style="@style/Widget.Vector.TextView.HeadlineMedium"
style="@style/Widget.Vector.TextView.Subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:layout_marginTop="8dp"
android:textColor="?vctr_content_primary"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
@ -22,7 +22,7 @@
android:id="@+id/optionsContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginTop="12dp"
android:divider="@drawable/divider_poll_options"
android:orientation="vertical"
android:showDividers="middle"