Skip to content
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

Merged
merged 36 commits into from
Dec 13, 2021
Merged

Poll Feature - Timeline #4624

merged 36 commits into from
Dec 13, 2021

Conversation

onurays
Copy link
Contributor

@onurays onurays commented Dec 3, 2021

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

Onuray Sahin added 13 commits November 9, 2021 13:19
* 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
  ...
@DoM1niC
Copy link

DoM1niC commented Dec 3, 2021

image
Circle need be colored but so far works perfectly for me :)

@onurays
Copy link
Contributor Author

onurays commented Dec 7, 2021

image Circle need be colored but so far works perfectly for me :)

Good catch, fixed.

* 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
@github-actions
Copy link

github-actions bot commented Dec 7, 2021

Unit Test Results

  66 files  ±0    66 suites  ±0   49s ⏱️ -7s
135 tests ±0  135 ✔️ ±0  0 💤 ±0  0 ±0 
418 runs  ±0  418 ✔️ ±0  0 💤 ±0  0 ±0 

Results for commit db81ec2. ± Comparison against base commit 5ea7f3c.

♻️ This comment has been updated with latest results.

@onurays onurays linked an issue Dec 7, 2021 that may be closed by this pull request
8 tasks
@onurays onurays requested review from bmarty and gaelledel December 7, 2021 17:14
@onurays onurays marked this pull request as ready for review December 7, 2021 17:16
Copy link
Member

@bmarty bmarty left a 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,
Copy link
Member

@bmarty bmarty Dec 7, 2021

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, nice catch. Done.

Copy link
Contributor Author

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"
Copy link
Member

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.

Copy link
Contributor Author

@onurays onurays Dec 8, 2021

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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct. MessagePollResponseContent must not inherit from MessageContent, since the type is not "m.room.message".
When I answer a poll from ElementWeb I got this FTR:

image

Copy link
Contributor Author

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
Copy link
Member

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?

Copy link
Contributor Author

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) {
Copy link
Member

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

Copy link
Contributor Author

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) {
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that you are right.

image

</shape>
</clip>
</item>
</layer-list>
Copy link
Member

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" />
Copy link
Member

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.

Copy link
Contributor Author

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>
Copy link
Member

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.

Copy link
Contributor Author

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>
Copy link
Member

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.

Copy link
Contributor Author

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.

@bmarty
Copy link
Member

bmarty commented Dec 9, 2021

When enabling "show hidden event" I see that:

image

  • Could be nice to add a better notice display here.

@onurays onurays requested a review from gaelledel December 10, 2021 14:58
Copy link
Member

@bmarty bmarty left a 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)
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link

@gaelledel gaelledel left a 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

Screenshot 2021-12-13 at 10 15 41

  • 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).

Screenshot 2021-12-13 at 10 17 54

  • We still have a problem with the option sizes. They need to reach the edge of the timestamp. See example here

Screenshot 2021-12-13 at 10 20 38

@gaelledel
Copy link

gaelledel commented Dec 13, 2021

@gaelledel it has been decided with @amshakal to add a right margin to all messages in the timeline, to avoid a blink effect because the last message has a send status which take some room on the right.

@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:
Screenshot_20211213-121004_Element dbg

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()
Copy link
Member

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.

Copy link
Contributor Author

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.

@bmarty
Copy link
Member

bmarty commented Dec 13, 2021

@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.

Copy link

@gaelledel gaelledel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved by me 🥳

@onurays onurays requested a review from bmarty December 13, 2021 14:42
* 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
Copy link
Member

@bmarty bmarty left a 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()
Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Member

@bmarty bmarty Dec 13, 2021

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

Copy link
Contributor Author

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 ->
Copy link
Member

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.

Copy link
Contributor Author

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) :
Copy link
Member

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:
image

(I think, not tested on your branch though)

Copy link
Contributor Author

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.

Copy link
Member

@bmarty bmarty left a 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
Copy link
Member

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(
Copy link
Member

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)
Copy link
Member

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!

@6543
Copy link

6543 commented Dec 13, 2021

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 ....))

@onurays
Copy link
Contributor Author

onurays commented Dec 13, 2021

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.

@bmarty bmarty force-pushed the feature/ons/poll_timeline branch from 8dff863 to 0981af3 Compare December 13, 2021 20:33
Copy link
Member

@bmarty bmarty left a 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.

@bmarty bmarty added the A-Polls label Dec 13, 2021
@bmarty bmarty merged commit 38e7e2f into develop Dec 13, 2021
@bmarty bmarty deleted the feature/ons/poll_timeline branch December 13, 2021 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Poll Feature - Render in timeline
5 participants