-
Notifications
You must be signed in to change notification settings - Fork 754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Poll Feature - Timeline #4624
Poll Feature - Timeline #4624
Changes from 2 commits
e0abd99
94ae3c6
eb15197
ebc131f
7c26930
a3b11b2
2a3a558
06485cf
32e8a7e
c62028d
0a7df44
435dc9f
23ad4e5
1df6b33
0d3444b
75b544a
71d7270
566f633
953fade
0f11e49
04a7590
b2e599e
d5f8931
be9e592
9b2a3cf
c7ad50a
f6dcda6
f028f98
e2bbc3f
c1ea653
eac06d5
10b39cc
da407ef
f29e14f
0981af3
db81ec2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,12 +18,12 @@ package im.vector.app.features.home.room.detail.timeline.item | |
|
||
import android.widget.LinearLayout | ||
import android.widget.TextView | ||
import androidx.core.view.children | ||
import com.airbnb.epoxy.EpoxyAttribute | ||
import com.airbnb.epoxy.EpoxyModelClass | ||
import im.vector.app.R | ||
import im.vector.app.features.home.room.detail.RoomDetailAction | ||
import im.vector.app.features.home.room.detail.timeline.TimelineEventController | ||
import org.matrix.android.sdk.api.extensions.orFalse | ||
import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent | ||
|
||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base) | ||
|
@@ -44,60 +44,44 @@ abstract class PollItem : AbsMessageItem<PollItem.Holder>() { | |
@EpoxyAttribute | ||
var pollSent: Boolean = false | ||
|
||
@EpoxyAttribute | ||
var totalVotesText: String? = null | ||
|
||
@EpoxyAttribute | ||
var optionViewStates: List<PollOptionViewState>? = null | ||
|
||
override fun bind(holder: Holder) { | ||
super.bind(holder) | ||
val relatedEventId = eventId ?: return | ||
|
||
renderSendState(holder.view, holder.questionTextView) | ||
|
||
holder.questionTextView.text = pollContent?.pollCreationInfo?.question?.question | ||
holder.totalVotesTextView.text = totalVotesText | ||
|
||
holder.optionsContainer.removeAllViews() | ||
val cachedViews = mutableMapOf<String, PollOptionItem>() | ||
holder.optionsContainer.children.filterIsInstance<PollOptionItem>().forEach { existingPollItemView -> | ||
cachedViews[existingPollItemView.getTag(STUB_ID)?.toString() ?: ""] = existingPollItemView | ||
} | ||
|
||
val isEnded = pollResponseSummary?.isClosed.orFalse() | ||
val didUserVoted = pollResponseSummary?.myVote?.isNotEmpty().orFalse() | ||
val totalVotes = pollResponseSummary?.totalVotes ?: 0 | ||
val winnerVoteCount = pollResponseSummary?.winnerVoteCount | ||
holder.optionsContainer.removeAllViews() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe avoid to remove (and re-add later) all the View? The number of views may differ. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just tested it and it is not working to remove items one by one before adding. Ends up with duplicated views. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Say you have 3 options to display. If there is:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see your point now. It will be an interesting experiment for me. |
||
|
||
pollContent?.pollCreationInfo?.answers?.forEach { option -> | ||
val voteSummary = pollResponseSummary?.votes?.get(option.id) | ||
val isMyVote = pollResponseSummary?.myVote == option.id | ||
val voteCount = voteSummary?.total ?: 0 | ||
val votePercentage = voteSummary?.percentage ?: 0.0 | ||
pollContent?.pollCreationInfo?.answers?.forEachIndexed { index, option -> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not iterate on
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
val optionName = option.answer ?: "" | ||
|
||
holder.optionsContainer.addView( | ||
PollOptionItem(holder.view.context).apply { | ||
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) | ||
} | ||
val tag = relatedEventId + option.id | ||
|
||
val pollOptionItem: PollOptionItem = (cachedViews[tag] ?: PollOptionItem(holder.view.context)) | ||
.apply { | ||
setTag(STUB_ID, tag) | ||
render( | ||
state = optionViewStates?.getOrNull(index) ?: PollOptionViewState.DisabledOptionWithInvisibleVotes(optionName) | ||
) | ||
} | ||
) | ||
} | ||
|
||
holder.totalVotesTextView.apply { | ||
text = when { | ||
isEnded -> resources.getQuantityString(R.plurals.poll_total_vote_count_after_ended, totalVotes, totalVotes) | ||
didUserVoted -> resources.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, totalVotes, totalVotes) | ||
else -> resources.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_not_voted, totalVotes, totalVotes) | ||
pollOptionItem.setOnClickListener { | ||
callback?.onTimelineItemAction(RoomDetailAction.VoteToPoll(relatedEventId, option.id ?: "")) | ||
} | ||
|
||
holder.optionsContainer.addView(pollOptionItem) | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,12 +32,7 @@ class PollOptionItem @JvmOverloads constructor( | |
defStyleAttr: Int = 0 | ||
) : ConstraintLayout(context, attrs, defStyleAttr) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't get it. It doesn't add a new level, just represents the current level, no? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
interface Callback { | ||
fun onOptionClicked() | ||
} | ||
|
||
private lateinit var views: ItemPollOptionBinding | ||
private var callback: Callback? = null | ||
|
||
init { | ||
setupViews() | ||
|
@@ -46,12 +41,9 @@ class PollOptionItem @JvmOverloads constructor( | |
private fun setupViews() { | ||
inflate(context, R.layout.item_poll_option, this) | ||
views = ItemPollOptionBinding.bind(this) | ||
|
||
views.root.setOnClickListener { callback?.onOptionClicked() } | ||
} | ||
|
||
fun render(state: PollOptionViewState, callback: Callback) { | ||
this.callback = callback | ||
fun render(state: PollOptionViewState) { | ||
|
||
views.optionNameTextView.text = state.name | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to update the action text too?
Currently this is:
(I think, not tested on your branch though)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, design team agrees to use the existing item.