-
Notifications
You must be signed in to change notification settings - Fork 743
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
Conversation
* feature/ons/poll: Design review fixes. Code review fixes. Make the poll option visible so that it can be tested from the PR Limit maximum number of poll options. Code review fixes. Fix UI issues. Remove poll command. Use unstable types. Create poll event content. Create poll UI implementation. Create poll fragment with a title. Create poll screen components implemented. Add poll icon to attachment type selector.
* feature/ons/poll: Hide poll icon from attachment type selector for now. Code review fixes. Lint fixes. Design review fixes.
* develop: (88 commits) Fix "PendingIntents attached to actions with remote inputs must be mutable" Room notifications are now working on Android 12 emulator Move P1 issues to app team and crypto boards Quick fix on this file Fix warning after rebase (it's for test, so OK to suppress warning) Update changelog, since the feature is not visible yet. Create file for Toast style (more generic) And improve fragment_create_poll.xml preview rendering Create CallToAction button style Create dedicated file for TextInputLayout styles And follow naming convention Lint fix. Add comment to run on Android 12 Use correct value, but I do not see any effect on emulator with API 12 Fix crash on Android 12. I guess we accept only images coming from the keyboard. ktlint Fix lint issue "NullSafeMutableLiveData" Fix lint issue "Incorrect constant" Fix lint issue "Outside Range" Ensure that column index is not -1 Make the Cursor extensions public And make the code more efficient, since we call getColumnIndexOrNull only once and not on each cursor iteration Fix crash on Android 12: PendingIntent.FLAG_IMMUTABLE has to be set Fix crash on Android 12 java.lang.SecurityException: To use the sampling rate of 0 microseconds, app needs to declare the normal permission HIGH_SAMPLING_RATE_SENSORS. InputConnectionCompat.createWrapper is deprecated Permission should be granted, according to https://developer.android.com/reference/android/view/OnReceiveContentListener#uri-permissions ...
* develop: (319 commits) Bump dagger from 2.40.4 to 2.40.5 More debouncing Changelog I need the view here clicks() already has debouncing with conflate(), so throttleFirst is not necessary Use debouncedClicks where applicable Use `observeViewEvents` facility private use != rather that is Fix waring for state is not dialing or connected state can also be connected Add script to compress video and convert to gif file removing boolean notification version usage adding changelog entry removing unused imports and increasing enum allowance porting the notifications setting version to the vector features Add warning if unexpected state. setting login version via typed build config field instead of resources separating the SSO redirection from the login activities - adds a dedicated routing activity to proxy the uri to the login selected by the feature flags Removing trailing space ... # Conflicts: # vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt # vector/src/main/java/im/vector/app/features/home/room/detail/timeline/format/DisplayableEventFormatter.kt # vector/src/main/res/layout/item_timeline_event_option_buttons_stub.xml # vector/src/main/res/xml/vector_settings_labs.xml
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.
Nice work!
The PR needs some cleanup before it can be merged, but there is not much to do.
Thanks!
@@ -24,8 +24,7 @@ import com.squareup.moshi.JsonClass | |||
*/ | |||
@JsonClass(generateAdapter = true) | |||
data class PollSummaryContent( | |||
// Index of my vote | |||
var myVote: Int? = null, | |||
var myVote: String? = null, |
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.
Strange to see var
in a data class. Also there it no @Json
annotations on the fields? Is it a Content of a Matrix message? If not if should be renamed to remove the Content
suffix.
EDIT: I think it is not the case, so @JsonClass(generateAdapter = true)
can also be removed.
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.
Right, nice catch. Done.
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.
Not done. We need it to be able to (de)serialize in DB as String.
const val MSGTYPE_RESPONSE = "org.matrix.response" | ||
const val MSGTYPE_POLL_CLOSED = "org.matrix.poll_closed" | ||
const val MSGTYPE_POLL_START = "org.matrix.msc3381.poll.start" | ||
const val MSGTYPE_POLL_RESPONSE = "org.matrix.msc3381.poll.response" |
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.
Those 2 strings are not msgType
, but EventType
. And they are already defined in EventType. So please remove.
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.
As we discussed, I changed it as we did for sticker events.
@JsonClass(generateAdapter = true) | ||
data class MessagePollResponseContent( | ||
@Json(name = MessageContent.MSG_TYPE_JSON_KEY) override val msgType: String = MessageType.MSGTYPE_RESPONSE, | ||
@Json(name = "body") override val body: String, | ||
override val msgType: String = MessageType.MSGTYPE_POLL_RESPONSE, |
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.
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.
As we discussed, I changed it as we did for sticker events.
* @return a [Cancelable] | ||
*/ | ||
fun sendOptionsReply(pollEventId: String, optionIndex: Int, optionValue: String): Cancelable | ||
fun registerVoteToPoll(pollEventId: String, optionKey: String): Cancelable |
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.
why register
prefix here and not just voteToPoll
? This is maybe coming from the spec?
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.
It was from task title :) Changed as suggested.
@@ -107,7 +111,7 @@ internal class EventRelationsAggregationProcessor @Inject constructor( | |||
Timber.v("###REPLACE in room $roomId for event ${event.eventId}") | |||
// A replace! | |||
handleReplace(realm, event, content, roomId, isLocalEcho) | |||
} else if (content?.relatesTo?.type == RelationType.RESPONSE) { | |||
} else if (content is MessagePollResponseContent) { |
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.
event.type
is not EventType.MESSAGE
. This code can be removed, it`s handled at line 205
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.
Of course. Good catch, removed.
context: Context, | ||
attrs: AttributeSet? = null, | ||
defStyleAttr: Int = 0 | ||
) : ConstraintLayout(context, attrs, defStyleAttr) { |
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.
item_poll_option.xml
root view is also a ConstraintLayout
, so the view hierarchy is too deep. You may remove a level.
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.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
</shape> | ||
</clip> | ||
</item> | ||
</layer-list> |
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.
Nice solution!
app:layout_constraintEnd_toEndOf="parent" | ||
app:layout_constraintStart_toStartOf="parent" | ||
app:layout_constraintTop_toTopOf="parent" | ||
tools:ignore="ContentDescription" /> |
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.
Please remove tools:ignore="ContentDescription"
from several place in this file (and maybe in other files). You should either add a contentDescription or use importantForAccessibility attribute.
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.
Right. This is the only file related to polls. Done.
</plurals> | ||
<plurals name="poll_total_vote_count_after_ended"> | ||
<item quantity="one">Final result based on %1$s vote</item> | ||
<item quantity="other">Final result based on %1$s votes</item> |
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.
I prefer that we use %1$d
instead of %1$s
when integers will be used to build the string. To be changed at 8 places.
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.
Of course. Done.
<string name="poll_end_action">End poll</string> | ||
<string name="a11y_poll_winner_option">winner option</string> | ||
<string name="end_poll_confirmation_title">End poll</string> | ||
<string name="end_poll_confirmation_description">Are you sure you want to end this poll? This will stop people from being able to vote and will display the final results of the poll.</string> |
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.
Saying "Are you sure" is bad for the user experience (too stressful).
The dialog title should be:
End this poll?
And the content:
Ending the poll will stop people from being able to vote and will display the final results of the poll.
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.
I agree. We may consider checking other places we have this. Done.
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.
Thanks for the update. One more remark. Will do some test too.
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) |
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.
That's better but the Epoxy item should have a field with a list of PollOptionViewState
now, so that all those computation are done outside the binding.
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.
Your suggestion is to calculate state outside of epoxy item and pass PollOptionViewState
to PollItem
as EpoxyAttribute
, right?
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, or rather a List<PollOptionViewState>
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.
Done.
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.
- Please change the copy on the Remove dialog so it matches the copy provided in Figma/below
- Can we have the same amount of padding on each side of the option frame? 8px should do. (currently it seems we have larger padding on the right).
- We still have a problem with the option sizes. They need to reach the edge of the timestamp. See example here
@bmarty Yes for text you might need a margin but it shouldn't affect other media types such as poll. See Andy's Read receipt in the image below: We're considerably wasting real estate for each option text length by doing this where the RR is actually not obstructing anything being under the options. |
|
||
holder.questionTextView.text = pollContent?.pollCreationInfo?.question?.question | ||
|
||
holder.optionsContainer.removeAllViews() |
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.
Should be better to avoid to do that each time, but rather get the existing views and recycle them.
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.
Done. We always need to remove & add views but I suggest a way to prevent the recreation of option views.
@gaelledel read receipt are below the messages, but send status are on the right of the message. We want to avoid the blink which can occur when the send status goes from shown to hidden (which occur as soon as another message appear in the timeline. |
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.
Approved by me 🥳
* develop: (49 commits) Update changelog.d/4592.bugfix Remove jcenter from here, let's see what the CI will say Common struct for each maven repo Rename the file to group them Add changelog file Revert PR 4592 for devtools Enhance include groups implementation by decoupling them to a separate file Cleanup Legals: improve API to get homeserver terms Legals: update setting icon Auto-review Add a help section in the settings. Changelog Color for links Use same height than the loading item to avoid dynamic resizing Legals: only display external URLs. Legals: Move the 2 copyrights items to the new legal screen Add some space between the 2 TextViews, and improve the layout Legals: Move the 3 element links to the new legal screen Rename some classes ... # Conflicts: # vector/src/main/res/layout/item_timeline_event_option_buttons_stub.xml # vector/src/main/res/layout/item_timeline_event_poll_stub.xml
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.
We are nearly there, some more comments!
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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
Say you have 3 options to display. If there is:
- 0 existing views: create 3 views and add it to the parent (this will be the most cases)
- 4 existing views: just remove one, and do not add any
- 3 existing views: do nothing
- 2 existing views: create 1 view and add it to the parent
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.
I see your point now. It will be an interesting experiment for me.
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 comment
The reason will be displayed to describe this comment to others. Learn more.
I would not iterate on pollContent?.pollCreationInfo?.answers?
anymore, but on optionViewStates
now.
pollContent
can be removed I think.
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.
Done.
@@ -60,7 +60,7 @@ sealed class EventSharedAction(@StringRes val titleRes: Int, | |||
data class Remove(val eventId: String) : | |||
EventSharedAction(R.string.remove, R.drawable.ic_trash, true) | |||
|
|||
data class Redact(val eventId: String, val askForReason: Boolean) : | |||
data class Redact(val eventId: String, val askForReason: Boolean, val dialogTitleRes: Int, val dialogDescriptionRes: Int) : |
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.
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.
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.
Some minor final remarks
var pollQuestion: String? = null | ||
|
||
@EpoxyAttribute | ||
var pollResponseSummary: PollResponseData? = null |
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.
This can be removed.
import im.vector.app.core.extensions.setAttributeTintedImageResource | ||
import im.vector.app.databinding.ItemPollOptionBinding | ||
|
||
class PollOptionItem @JvmOverloads constructor( |
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.
Class with Item
suffix is reserved for Epoxy items. Please rename to PollOptionView
is PollOptionViewState.DisabledOptionWithInvisibleVotes -> renderDisabledOptionWithInvisibleVotes() | ||
is PollOptionViewState.DisabledOptionWithVisibleVotes -> renderDisabledOptionWithVisibleVotes(state) | ||
is PollOptionViewState.EnabledOptionWithInvisibleVotes -> renderEnabledOptionWithInvisibleVotes() | ||
is PollOptionViewState.EnabledOptionWithVisibleVotes -> renderEnabledOptionWithVisibleVotes(state) |
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.
The code is much cleaner now, thanks!
hey just stumbled over this pull - a smal question about this feature pr: is there a option to add new options to choose (not sure how ui should look like (to toggle other to allow that or deny ....)) |
Editing polls is not a part of this PR but it is planned/designed as post MVP. |
8dff863
to
0981af3
Compare
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.
Approved, I have added some commits, you tell me if you agree with the changes.
Design: https://www.figma.com/file/tepA3ELWBE5v4O2WB8PqWn/Polls?node-id=247%3A62813
Users cannot see votes until they vote. But the total casted vote count is visible
Users can end poll if they have enough power level
Multiple options can win if they have the same amount of votes (bigger than zero)
Creating and rendering polls are under Labs and disabled by default