mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 10:25:35 +03:00
Code review fixes.
This commit is contained in:
parent
be9e592aa5
commit
9b2a3cf445
11 changed files with 158 additions and 83 deletions
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ?: ""
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue