Compare commits
8 Commits
develop
...
feature/on
Author | SHA1 | Date |
---|---|---|
Onuray Sahin | c40577c0a2 | |
Onuray Sahin | f2359ccac2 | |
Onuray Sahin | 89a7d70849 | |
Onuray Sahin | 89f91a2ecd | |
Onuray Sahin | b53615a8d7 | |
Onuray Sahin | 374445eed6 | |
Onuray Sahin | 486968fdc2 | |
Onuray Sahin | f6cabfffd9 |
|
@ -0,0 +1 @@
|
|||
Render ended polls
|
|
@ -3190,6 +3190,7 @@
|
|||
<string name="open_poll_option_description">Voters see results as soon as they have voted</string>
|
||||
<string name="closed_poll_option_title">Closed poll</string>
|
||||
<string name="closed_poll_option_description">Results are only revealed when you end the poll</string>
|
||||
<string name="ended_poll_indicator">Ended the poll.</string>
|
||||
|
||||
<!-- Location -->
|
||||
<string name="location_activity_title_static_sharing">Share location</string>
|
||||
|
@ -3501,4 +3502,6 @@
|
|||
<string name="message_reply_to_sender_sent_video">sent a video.</string>
|
||||
<string name="message_reply_to_sender_sent_sticker">sent a sticker.</string>
|
||||
<string name="message_reply_to_sender_created_poll">created a poll.</string>
|
||||
<string name="message_reply_to_sender_ended_poll">ended a poll.</string>
|
||||
<string name="message_reply_to_ended_poll_preview">Ended poll</string>
|
||||
</resources>
|
||||
|
|
|
@ -219,7 +219,7 @@ data class Event(
|
|||
if (isRedacted()) return "Message removed"
|
||||
val text = getDecryptedValue() ?: run {
|
||||
if (isPoll()) {
|
||||
return getPollQuestion() ?: "created a poll."
|
||||
return getPollQuestion() ?: if (isPollStart()) "created a poll." else if (isPollEnd()) "ended a poll." else null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ data class Event(
|
|||
isImageMessage() -> "sent an image."
|
||||
isVideoMessage() -> "sent a video."
|
||||
isSticker() -> "sent a sticker."
|
||||
isPoll() -> getPollQuestion() ?: "created a poll."
|
||||
isPoll() -> getPollQuestion() ?: if (isPollStart()) "created a poll." else if (isPollEnd()) "ended a poll." else null
|
||||
isLiveLocation() -> "Live location."
|
||||
isLocationMessage() -> "has shared their location."
|
||||
else -> text
|
||||
|
|
|
@ -18,6 +18,7 @@ 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.Content
|
||||
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
|
||||
|
||||
/**
|
||||
|
@ -25,5 +26,12 @@ import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultCon
|
|||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class MessageEndPollContent(
|
||||
@Json(name = "m.relates_to") val relatesTo: RelationDefaultContent? = null
|
||||
)
|
||||
/**
|
||||
* Local message type, not from server.
|
||||
*/
|
||||
@Transient
|
||||
override val msgType: String = MessageType.MSGTYPE_POLL_END,
|
||||
@Json(name = "body") override val body: String = "",
|
||||
@Json(name = "m.new_content") override val newContent: Content? = null,
|
||||
@Json(name = "m.relates_to") override val relatesTo: RelationDefaultContent? = null
|
||||
) : MessageContent
|
||||
|
|
|
@ -36,6 +36,7 @@ object MessageType {
|
|||
// Because poll events are not message events and they don't have msgtype field
|
||||
const val MSGTYPE_POLL_START = "org.matrix.android.sdk.poll.start"
|
||||
const val MSGTYPE_POLL_RESPONSE = "org.matrix.android.sdk.poll.response"
|
||||
const val MSGTYPE_POLL_END = "org.matrix.android.sdk.poll.end"
|
||||
|
||||
const val MSGTYPE_CONFETTI = "nic.custom.confetti"
|
||||
const val MSGTYPE_SNOWFALL = "io.element.effect.snowfall"
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoCo
|
|||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconLocationDataContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||
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
|
||||
|
@ -148,6 +149,7 @@ fun TimelineEvent.getLastMessageContent(): MessageContent? {
|
|||
// so toModel<MessageContent> won't parse them correctly
|
||||
// It's discriminated on event type instead. Maybe it shouldn't be MessageContent at all to avoid confusion?
|
||||
in EventType.POLL_START.values -> (getLastEditNewContent() ?: root.getClearContent()).toModel<MessagePollContent>()
|
||||
in EventType.POLL_END.values -> (getLastEditNewContent() ?: root.getClearContent()).toModel<MessageEndPollContent>()
|
||||
in EventType.STATE_ROOM_BEACON_INFO.values -> (getLastEditNewContent() ?: root.getClearContent()).toModel<MessageBeaconInfoContent>()
|
||||
in EventType.BEACON_LOCATION_DATA.values -> (getLastEditNewContent() ?: root.getClearContent()).toModel<MessageBeaconLocationDataContent>()
|
||||
else -> (getLastEditNewContent() ?: root.getClearContent()).toModel()
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent
|
|||
|
||||
fun TimelineEvent.canReact(): Boolean {
|
||||
// Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment
|
||||
return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START.values &&
|
||||
return root.getClearType() in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START.values + EventType.POLL_END.values &&
|
||||
root.sendState == SendState.SYNCED &&
|
||||
!root.isRedacted()
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ import org.commonmark.parser.Parser
|
|||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageFormat
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent
|
||||
|
@ -181,6 +182,7 @@ class PlainTextComposerLayout @JvmOverloads constructor(
|
|||
is MessageAudioContent -> getAudioContentBodyText(messageContent)
|
||||
is MessagePollContent -> messageContent.getBestPollCreationInfo()?.question?.getBestQuestion()
|
||||
is MessageBeaconInfoContent -> resources.getString(R.string.live_location_description)
|
||||
is MessageEndPollContent -> resources.getString(R.string.message_reply_to_ended_poll_preview)
|
||||
else -> messageContent?.body.orEmpty()
|
||||
}
|
||||
var formattedBody: CharSequence? = null
|
||||
|
|
|
@ -25,8 +25,14 @@ import javax.inject.Inject
|
|||
class CheckIfCanReplyEventUseCase @Inject constructor() {
|
||||
|
||||
fun execute(event: TimelineEvent, messageContent: MessageContent?, actionPermissions: ActionPermissions): Boolean {
|
||||
// Only EventType.MESSAGE, EventType.POLL_START and EventType.STATE_ROOM_BEACON_INFO event types are supported for the moment
|
||||
if (event.root.getClearType() !in EventType.STATE_ROOM_BEACON_INFO.values + EventType.POLL_START.values + EventType.MESSAGE) return false
|
||||
// Only EventType.MESSAGE, EventType.POLL_START, EventType.POLL_END and EventType.STATE_ROOM_BEACON_INFO event types are supported for the moment
|
||||
if (event.root.getClearType() !in
|
||||
EventType.STATE_ROOM_BEACON_INFO.values +
|
||||
EventType.POLL_START.values +
|
||||
EventType.POLL_END.values +
|
||||
EventType.MESSAGE
|
||||
) return false
|
||||
|
||||
if (!actionPermissions.canSendMessage) return false
|
||||
return when (messageContent?.msgType) {
|
||||
MessageType.MSGTYPE_TEXT,
|
||||
|
@ -37,6 +43,7 @@ class CheckIfCanReplyEventUseCase @Inject constructor() {
|
|||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_FILE,
|
||||
MessageType.MSGTYPE_POLL_START,
|
||||
MessageType.MSGTYPE_POLL_END,
|
||||
MessageType.MSGTYPE_BEACON_INFO,
|
||||
MessageType.MSGTYPE_LOCATION -> true
|
||||
else -> false
|
||||
|
|
|
@ -498,6 +498,7 @@ class MessageActionsViewModel @AssistedInject constructor(
|
|||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_FILE,
|
||||
MessageType.MSGTYPE_POLL_START,
|
||||
MessageType.MSGTYPE_POLL_END,
|
||||
MessageType.MSGTYPE_STICKER_LOCAL -> event.root.threadDetails?.isRootThread ?: false
|
||||
else -> false
|
||||
}
|
||||
|
@ -529,8 +530,8 @@ class MessageActionsViewModel @AssistedInject constructor(
|
|||
}
|
||||
|
||||
private fun canViewReactions(event: TimelineEvent): Boolean {
|
||||
// Only event of type EventType.MESSAGE, EventType.STICKER and EventType.POLL_START are supported for the moment
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START.values) return false
|
||||
// Only event of type EventType.MESSAGE, EventType.STICKER, EventType.POLL_START, EventType.POLL_END are supported for the moment
|
||||
if (event.root.getClearType() !in listOf(EventType.MESSAGE, EventType.STICKER) + EventType.POLL_START.values + EventType.POLL_END.values) return false
|
||||
return event.annotations?.reactionsSummary?.isNotEmpty() ?: false
|
||||
}
|
||||
|
||||
|
|
|
@ -91,11 +91,13 @@ import org.matrix.android.sdk.api.session.events.model.RelationType
|
|||
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
|
||||
import org.matrix.android.sdk.api.session.events.model.isThread
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.getTimelineEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageBeaconInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageContentWithFormattedBody
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEmoteContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageEndPollContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageFileContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageImageInfoContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageLocationContent
|
||||
|
@ -202,7 +204,8 @@ class MessageItemFactory @Inject constructor(
|
|||
is MessageFileContent -> buildFileMessageItem(messageContent, highlight, attributes)
|
||||
is MessageAudioContent -> buildAudioContent(params, messageContent, informationData, highlight, attributes)
|
||||
is MessageVerificationRequestContent -> buildVerificationRequestMessageItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessagePollContent -> buildPollItem(messageContent, informationData, highlight, callback, attributes, isEnded = false)
|
||||
is MessageEndPollContent -> buildEndedPollItem(messageContent, informationData, highlight, callback, attributes)
|
||||
is MessageLocationContent -> buildLocationItem(messageContent, informationData, highlight, attributes)
|
||||
is MessageBeaconInfoContent -> liveLocationShareMessageItemFactory.create(event, highlight, attributes)
|
||||
is MessageVoiceBroadcastInfoContent -> voiceBroadcastItemFactory.create(params, messageContent, highlight, attributes)
|
||||
|
@ -245,6 +248,7 @@ class MessageItemFactory @Inject constructor(
|
|||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?,
|
||||
attributes: AbsMessageItem.Attributes,
|
||||
isEnded: Boolean,
|
||||
): PollItem {
|
||||
val pollViewState = pollItemViewStateFactory.create(pollContent, informationData)
|
||||
|
||||
|
@ -256,11 +260,39 @@ class MessageItemFactory @Inject constructor(
|
|||
.votesStatus(pollViewState.votesStatus)
|
||||
.optionViewStates(pollViewState.optionViewStates.orEmpty())
|
||||
.edited(informationData.hasBeenEdited)
|
||||
.ended(isEnded)
|
||||
.highlighted(highlight)
|
||||
.leftGuideline(avatarSizeProvider.leftGuideline)
|
||||
.callback(callback)
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun buildEndedPollItem(
|
||||
endedPollContent: MessageEndPollContent,
|
||||
informationData: MessageInformationData,
|
||||
highlight: Boolean,
|
||||
callback: TimelineEventController.Callback?,
|
||||
attributes: AbsMessageItem.Attributes,
|
||||
): PollItem? {
|
||||
val pollStartEventId = endedPollContent.relatesTo?.eventId ?: return null
|
||||
val pollStartEvent = session.roomService().getRoom(roomId)?.getTimelineEvent(pollStartEventId)
|
||||
val pollContent = pollStartEvent?.root?.getClearContent()?.toModel<MessagePollContent>() ?: return null
|
||||
|
||||
val aggregatedInformationData = informationData.copy(
|
||||
pollResponseAggregatedSummary = messageInformationDataFactory.mapPollResponseSummary(pollStartEvent.annotations?.pollResponseSummary)
|
||||
)
|
||||
|
||||
return buildPollItem(
|
||||
pollContent,
|
||||
aggregatedInformationData,
|
||||
highlight,
|
||||
callback,
|
||||
attributes,
|
||||
isEnded = true
|
||||
)
|
||||
}
|
||||
|
||||
private fun createPollQuestion(
|
||||
informationData: MessageInformationData,
|
||||
question: String,
|
||||
|
|
|
@ -102,6 +102,7 @@ class TimelineItemFactory @Inject constructor(
|
|||
// Message itemsX
|
||||
EventType.STICKER,
|
||||
in EventType.POLL_START.values,
|
||||
in EventType.POLL_END.values,
|
||||
EventType.MESSAGE -> messageItemFactory.create(params)
|
||||
EventType.REDACTION,
|
||||
EventType.KEY_VERIFICATION_ACCEPT,
|
||||
|
@ -114,8 +115,7 @@ class TimelineItemFactory @Inject constructor(
|
|||
EventType.CALL_SELECT_ANSWER,
|
||||
EventType.CALL_NEGOTIATE,
|
||||
EventType.REACTION,
|
||||
in EventType.POLL_RESPONSE.values,
|
||||
in EventType.POLL_END.values -> noticeItemFactory.create(params)
|
||||
in EventType.POLL_RESPONSE.values -> noticeItemFactory.create(params)
|
||||
in EventType.BEACON_LOCATION_DATA.values -> {
|
||||
if (event.root.isRedacted()) {
|
||||
messageItemFactory.create(params)
|
||||
|
|
|
@ -17,11 +17,13 @@
|
|||
package im.vector.app.features.home.room.detail.timeline.format
|
||||
|
||||
import android.content.Context
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.utils.TextUtils
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.isAudioMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isFileMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isImageMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isPollEnd
|
||||
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||
|
@ -51,10 +53,13 @@ class EventDetailsFormatter @Inject constructor(
|
|||
event.isVideoMessage() -> formatForVideoMessage(event)
|
||||
event.isAudioMessage() -> formatForAudioMessage(event)
|
||||
event.isFileMessage() -> formatForFileMessage(event)
|
||||
event.isPollEnd() -> formatPollEndMessage()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatPollEndMessage() = context.getString(R.string.message_reply_to_ended_poll_preview)
|
||||
|
||||
/**
|
||||
* Example: "1024 x 720 - 670 kB".
|
||||
*/
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.session.events.model.isAttachmentMessage
|
|||
import org.matrix.android.sdk.api.session.events.model.isSticker
|
||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||
import org.matrix.android.sdk.api.session.events.model.toValidDecryptedEvent
|
||||
import org.matrix.android.sdk.api.session.room.model.PollResponseAggregatedSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.ReferencesAggregatedContent
|
||||
import org.matrix.android.sdk.api.session.room.model.RoomSummary
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageType
|
||||
|
@ -99,20 +100,7 @@ class MessageInformationDataFactory @Inject constructor(
|
|||
memberName = event.senderInfo.disambiguatedDisplayName,
|
||||
messageLayout = messageLayout,
|
||||
reactionsSummary = reactionsSummaryFactory.create(event),
|
||||
pollResponseAggregatedSummary = event.annotations?.pollResponseSummary?.let {
|
||||
PollResponseData(
|
||||
myVote = it.aggregatedContent?.myVote,
|
||||
isClosed = it.closedTime != null,
|
||||
votes = it.aggregatedContent?.votesSummary?.mapValues { votesSummary ->
|
||||
PollVoteSummaryData(
|
||||
total = votesSummary.value.total,
|
||||
percentage = votesSummary.value.percentage
|
||||
)
|
||||
},
|
||||
winnerVoteCount = it.aggregatedContent?.winnerVoteCount ?: 0,
|
||||
totalVotes = it.aggregatedContent?.totalVotes ?: 0
|
||||
)
|
||||
},
|
||||
pollResponseAggregatedSummary = mapPollResponseSummary(event.annotations?.pollResponseSummary),
|
||||
hasBeenEdited = event.hasBeenEdited(),
|
||||
hasPendingEdits = event.annotations?.editSummary?.localEchos?.any() ?: false,
|
||||
referencesInfoData = event.annotations?.referencesAggregatedSummary?.let { referencesAggregatedSummary ->
|
||||
|
@ -133,6 +121,23 @@ class MessageInformationDataFactory @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
fun mapPollResponseSummary(pollResponseSummary: PollResponseAggregatedSummary?): PollResponseData? {
|
||||
return pollResponseSummary?.let {
|
||||
PollResponseData(
|
||||
myVote = it.aggregatedContent?.myVote,
|
||||
isClosed = it.closedTime != null,
|
||||
votes = it.aggregatedContent?.votesSummary?.mapValues { votesSummary ->
|
||||
PollVoteSummaryData(
|
||||
total = votesSummary.value.total,
|
||||
percentage = votesSummary.value.percentage
|
||||
)
|
||||
},
|
||||
winnerVoteCount = it.aggregatedContent?.winnerVoteCount ?: 0,
|
||||
totalVotes = it.aggregatedContent?.totalVotes ?: 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSenderId(event: TimelineEvent) = if (event.isEncrypted()) {
|
||||
event.root.toValidDecryptedEvent()?.let {
|
||||
session.cryptoService().deviceWithIdentityKey(it.cryptoSenderKey, it.algorithm)?.userId
|
||||
|
|
|
@ -55,6 +55,7 @@ object TimelineDisplayableEvents {
|
|||
VoiceBroadcastConstants.STATE_ROOM_VOICE_BROADCAST_INFO,
|
||||
) +
|
||||
EventType.POLL_START.values +
|
||||
EventType.POLL_END.values +
|
||||
EventType.STATE_ROOM_BEACON_INFO.values +
|
||||
EventType.BEACON_LOCATION_DATA.values
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail.timeline.item
|
|||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
|
@ -50,6 +51,9 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
|
|||
@EpoxyAttribute
|
||||
lateinit var optionViewStates: List<PollOptionViewState>
|
||||
|
||||
@EpoxyAttribute
|
||||
var ended: Boolean = false
|
||||
|
||||
override fun getViewStubId() = STUB_ID
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
|
@ -75,6 +79,8 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
|
|||
it.setOnClickListener { onPollItemClick(optionViewState) }
|
||||
}
|
||||
}
|
||||
|
||||
holder.endedPollTextView.isVisible = ended
|
||||
}
|
||||
|
||||
private fun onPollItemClick(optionViewState: PollOptionViewState) {
|
||||
|
@ -89,6 +95,7 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() {
|
|||
val questionTextView by bind<TextView>(R.id.questionTextView)
|
||||
val optionsContainer by bind<LinearLayout>(R.id.optionsContainer)
|
||||
val votesStatusTextView by bind<TextView>(R.id.optionsVotesStatusTextView)
|
||||
val endedPollTextView by bind<TextView>(R.id.endedPollTextView)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -53,35 +53,36 @@ class PollOptionView @JvmOverloads constructor(
|
|||
|
||||
private fun renderPollSending() {
|
||||
views.optionCheckImageView.isVisible = false
|
||||
views.optionWinnerImageView.isVisible = false
|
||||
views.optionVoteCountTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
||||
hideVotes()
|
||||
renderVoteSelection(false)
|
||||
}
|
||||
|
||||
private fun renderPollEnded(state: PollOptionViewState.PollEnded) {
|
||||
views.optionCheckImageView.isVisible = false
|
||||
views.optionWinnerImageView.isVisible = state.isWinner
|
||||
val drawableStart = if (state.isWinner) R.drawable.ic_poll_winner else 0
|
||||
views.optionVoteCountTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStart, 0, 0, 0)
|
||||
showVotes(state.voteCount, state.votePercentage)
|
||||
renderVoteSelection(state.isWinner)
|
||||
}
|
||||
|
||||
private fun renderPollReady() {
|
||||
views.optionCheckImageView.isVisible = true
|
||||
views.optionWinnerImageView.isVisible = false
|
||||
views.optionVoteCountTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
||||
hideVotes()
|
||||
renderVoteSelection(false)
|
||||
}
|
||||
|
||||
private fun renderPollVoted(state: PollOptionViewState.PollVoted) {
|
||||
views.optionCheckImageView.isVisible = true
|
||||
views.optionWinnerImageView.isVisible = false
|
||||
views.optionVoteCountTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
||||
showVotes(state.voteCount, state.votePercentage)
|
||||
renderVoteSelection(state.isSelected)
|
||||
}
|
||||
|
||||
private fun renderPollUndisclosed(state: PollOptionViewState.PollUndisclosed) {
|
||||
views.optionCheckImageView.isVisible = true
|
||||
views.optionWinnerImageView.isVisible = false
|
||||
views.optionVoteCountTextView.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, 0, 0)
|
||||
hideVotes()
|
||||
renderVoteSelection(state.isSelected)
|
||||
}
|
||||
|
|
|
@ -19,12 +19,15 @@ package im.vector.app.features.home.room.detail.timeline.render
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.getPollQuestion
|
||||
import org.matrix.android.sdk.api.session.events.model.isAudioMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isFileMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isImageMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isLiveLocation
|
||||
import org.matrix.android.sdk.api.session.events.model.isPoll
|
||||
import org.matrix.android.sdk.api.session.events.model.isPollEnd
|
||||
import org.matrix.android.sdk.api.session.events.model.isPollStart
|
||||
import org.matrix.android.sdk.api.session.events.model.isSticker
|
||||
import org.matrix.android.sdk.api.session.events.model.isVideoMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isVoiceMessage
|
||||
|
@ -93,10 +96,15 @@ class ProcessBodyOfReplyToEventUseCase @Inject constructor(
|
|||
)
|
||||
}
|
||||
repliedToEvent.isPoll() -> {
|
||||
val fallbackText = when {
|
||||
repliedToEvent.isPollStart() -> stringProvider.getString(R.string.message_reply_to_sender_created_poll)
|
||||
repliedToEvent.isPollEnd() -> stringProvider.getString(R.string.message_reply_to_sender_ended_poll)
|
||||
else -> ""
|
||||
}
|
||||
matrixFormattedBody.replaceRange(
|
||||
afterBreakingLineIndex,
|
||||
endOfBlockQuoteIndex,
|
||||
repliedToEvent.getPollQuestion() ?: stringProvider.getString(R.string.message_reply_to_sender_created_poll)
|
||||
repliedToEvent.getPollQuestion() ?: fallbackText
|
||||
)
|
||||
}
|
||||
repliedToEvent.isLiveLocation() -> {
|
||||
|
|
|
@ -50,6 +50,7 @@ class TimelineMessageLayoutFactory @Inject constructor(
|
|||
EventType.STICKER,
|
||||
) +
|
||||
EventType.POLL_START.values +
|
||||
EventType.POLL_END.values +
|
||||
EventType.STATE_ROOM_BEACON_INFO.values
|
||||
|
||||
// Can't be rendered in bubbles, so get back to default layout
|
||||
|
|
|
@ -36,34 +36,23 @@
|
|||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
app:layout_constraintEnd_toEndOf="@id/optionWinnerImageView"
|
||||
app:layout_constraintEnd_toStartOf="@id/optionVoteCountTextView"
|
||||
app:layout_constraintStart_toEndOf="@id/optionCheckImageView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@sample/poll.json/data/answer" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/optionWinnerImageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:contentDescription="@string/a11y_poll_winner_option"
|
||||
android:src="@drawable/ic_poll_winner"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/optionVoteCountTextView"
|
||||
style="@style/Widget.Vector.TextView.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:drawablePadding="6dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/optionVoteProgress"
|
||||
app:layout_constraintBottom_toBottomOf="@id/optionNameTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/optionVoteProgress"
|
||||
app:layout_constraintTop_toTopOf="@id/optionNameTextView"
|
||||
tools:drawableStartCompat="@drawable/ic_poll_winner"
|
||||
tools:text="@sample/poll.json/data/votes"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
@ -78,9 +67,9 @@
|
|||
android:layout_marginBottom="8dp"
|
||||
android:progressDrawable="@drawable/poll_option_progressbar_checked"
|
||||
app:layout_constraintBottom_toBottomOf="@id/optionBorderImageView"
|
||||
app:layout_constraintEnd_toStartOf="@id/optionVoteCountTextView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/optionNameTextView"
|
||||
tools:progress="60" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -2,9 +2,21 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:minWidth="@dimen/chat_bubble_fixed_size"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="@dimen/chat_bubble_fixed_size">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/endedPollTextView"
|
||||
style="@style/Widget.Vector.TextView.Caption"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/ended_poll_indicator"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/questionTextView"
|
||||
|
@ -13,11 +25,10 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="?vctr_content_primary"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/endedPollTextView"
|
||||
tools:text="@sample/poll.json/question" />
|
||||
|
||||
<LinearLayout
|
||||
|
|
|
@ -78,6 +78,7 @@ class CheckIfCanReplyEventUseCaseTest {
|
|||
MessageType.MSGTYPE_AUDIO,
|
||||
MessageType.MSGTYPE_FILE,
|
||||
MessageType.MSGTYPE_POLL_START,
|
||||
MessageType.MSGTYPE_POLL_END,
|
||||
MessageType.MSGTYPE_BEACON_INFO,
|
||||
MessageType.MSGTYPE_LOCATION
|
||||
)
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.junit.After
|
|||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.matrix.android.sdk.api.session.events.model.Event
|
||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||
import org.matrix.android.sdk.api.session.events.model.getPollQuestion
|
||||
import org.matrix.android.sdk.api.session.events.model.isAudioMessage
|
||||
import org.matrix.android.sdk.api.session.events.model.isFileMessage
|
||||
|
@ -158,6 +159,7 @@ class ProcessBodyOfReplyToEventUseCaseTest {
|
|||
// Given
|
||||
givenTypeOfRepliedEvent(isPollMessage = true)
|
||||
givenNewContentForId(R.string.message_reply_to_sender_created_poll)
|
||||
every { fakeRepliedEvent.type } returns EventType.POLL_START.unstable
|
||||
every { fakeRepliedEvent.getPollQuestion() } returns null
|
||||
|
||||
executeAndAssertResult()
|
||||
|
@ -168,6 +170,7 @@ class ProcessBodyOfReplyToEventUseCaseTest {
|
|||
// Given
|
||||
givenTypeOfRepliedEvent(isPollMessage = true)
|
||||
givenNewContentForId(R.string.message_reply_to_sender_created_poll)
|
||||
every { fakeRepliedEvent.type } returns EventType.POLL_START.unstable
|
||||
every { fakeRepliedEvent.getPollQuestion() } returns A_NEW_CONTENT
|
||||
|
||||
executeAndAssertResult()
|
||||
|
|
Loading…
Reference in New Issue