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

Add support for Polls Widget on ZT. #1551

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/hotkeys.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
|Toggle star status of the current message|<kbd>Ctrl</kbd> + <kbd>s</kbd> / <kbd>*</kbd>|
|Show/hide message information|<kbd>i</kbd>|
|Show/hide message sender information|<kbd>u</kbd>|
|Show/hide poll voter list|<kbd>v</kbd>|

## Stream list actions
|Command|Key Combination|
Expand Down
322 changes: 321 additions & 1 deletion tests/widget/test_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
import pytest
from pytest import param as case

from zulipterminal.widget import Submessage, find_widget_type, process_todo_widget
from zulipterminal.widget import (
Submessage,
find_widget_type,
process_poll_widget,
process_todo_widget,
)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -343,3 +348,318 @@ def test_process_todo_widget(

assert title == expected_title
assert tasks == expected_tasks


@pytest.mark.parametrize(
"submessage, expected_poll_question, expected_options",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: plural, as in other PR.

[
case(
[
{
"id": 12082,
"message_id": 1957499,
"sender_id": 27294,
"msg_type": "widget",
"content": (
'{"widget_type": "poll", "extra_data": {'
'"question": "Do polls work on ZT?", "options": ["Yes", "No"]}}'
),
},
{
"id": 12083,
"message_id": 1957499,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
{
"id": 12084,
"message_id": 1957499,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":-1}',
},
{
"id": 12085,
"message_id": 1957499,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,1","vote":1}',
},
{
"id": 12086,
"message_id": 1957499,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
{
"id": 12087,
"message_id": 1957499,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,1","vote":-1}',
},
],
"Do polls work on ZT?",
{
"canned,0": {"option": "Yes", "votes": [27294]},
"canned,1": {"option": "No", "votes": []},
},
id="poll_widget_with_votes",
),
case(
[
{
"id": 12089,
"message_id": 1957662,
"sender_id": 27294,
"msg_type": "widget",
"content": (
'{"widget_type": "poll", "extra_data": {"question": "Is '
'this a poll with options added later?", '
'"options": ["Yes", "No"]}}'
),
},
{
"id": 12090,
"message_id": 1957662,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"new_option","idx":1,"option":"Maybe"}',
},
{
"id": 12091,
"message_id": 1957662,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,1","vote":1}',
},
{
"id": 12092,
"message_id": 1957662,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,1","vote":-1}',
},
{
"id": 12093,
"message_id": 1957662,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"27294,1","vote":1}',
},
{
"id": 12094,
"message_id": 1957662,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
],
"Is this a poll with options added later?",
{
"canned,0": {"option": "Yes", "votes": [27294]},
"canned,1": {"option": "No", "votes": []},
"27294,1": {"option": "Maybe", "votes": [27294]},
},
id="poll_widget_with_new_option_and_votes",
),
case(
[
{
"id": 12095,
"message_id": 1957682,
"sender_id": 27294,
"msg_type": "widget",
"content": (
'{"widget_type": "poll", "extra_data": {"question": '
'"Let\'s change this question later?", "options": ["Yes"]}}'
),
},
{
"id": 12096,
"message_id": 1957682,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
{
"id": 12097,
"message_id": 1957682,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"new_option","idx":1,"option":"No"}',
},
{
"id": 12098,
"message_id": 1957682,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":-1}',
},
{
"id": 12099,
"message_id": 1957682,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"question",'
'"question":"Has this question stayed the same?"}',
},
{
"id": 12100,
"message_id": 1957682,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"27294,1","vote":1}',
},
],
"Has this question stayed the same?",
{
"canned,0": {"option": "Yes", "votes": []},
"27294,1": {"option": "No", "votes": [27294]},
},
id="poll_widget_with_new_question_and_votes",
),
case(
[
{
"id": 12101,
"message_id": 1957693,
"sender_id": 27294,
"msg_type": "widget",
"content": (
'{"widget_type": "poll", "extra_data": {"question": "",'
' "options": ["Yes", "No"]}}'
),
}
],
"",
{
"canned,0": {"option": "Yes", "votes": []},
"canned,1": {"option": "No", "votes": []},
},
id="poll_widget_with_empty_question",
),
case(
[
{
"id": 12102,
"message_id": 1957700,
"sender_id": 27294,
"msg_type": "widget",
"content": (
'{"widget_type": "poll", "extra_data": {'
'"question": "Does this poll have options?", "options": []}}'
),
}
],
"Does this poll have options?",
{},
id="poll_widget_with_empty_options",
),
case(
[
{
"id": 12112,
"message_id": 1957722,
"sender_id": 27294,
"msg_type": "widget",
"content": (
'{"widget_type": "poll", "extra_data": {"question": "",'
' "options": []}}'
),
}
],
"",
{},
id="poll_widget_with_empty_question_and_options",
),
case(
[
{
"id": 12103,
"message_id": 1957719,
"sender_id": 27294,
"msg_type": "widget",
"content": (
'{"widget_type": "poll", "extra_data": {"question": "Does'
' this poll have multiple voters?", "options": ["Yes", "No"]}}'
),
},
{
"id": 12104,
"message_id": 1957719,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
{
"id": 12105,
"message_id": 1957719,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,1","vote":1}',
},
{
"id": 12106,
"message_id": 1957719,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":-1}',
},
{
"id": 12107,
"message_id": 1957719,
"sender_id": 32159,
"msg_type": "widget",
"content": '{"type":"new_option","idx":1,"option":"Maybe"}',
},
{
"id": 12108,
"message_id": 1957719,
"sender_id": 32159,
"msg_type": "widget",
"content": '{"type":"vote","key":"32159,1","vote":1}',
},
{
"id": 12109,
"message_id": 1957719,
"sender_id": 32159,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
{
"id": 12110,
"message_id": 1957719,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,1","vote":-1}',
},
{
"id": 12111,
"message_id": 1957719,
"sender_id": 27294,
"msg_type": "widget",
"content": '{"type":"vote","key":"canned,0","vote":1}',
},
],
"Does this poll have multiple voters?",
{
"canned,0": {"option": "Yes", "votes": [32159, 27294]},
"canned,1": {"option": "No", "votes": []},
"32159,1": {"option": "Maybe", "votes": [32159]},
},
id="poll_widget_with_multiple_voters",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: This test is known to be a poll_widget already, as with other PR.

),
],
)
def test_process_poll_widget(
submessage: List[Submessage],
expected_poll_question: str,
expected_options: Dict[str, Dict[str, Union[str, List[str]]]],
) -> None:
poll_question, options = process_poll_widget(submessage)

assert poll_question == expected_poll_question
assert options == expected_options
5 changes: 5 additions & 0 deletions zulipterminal/config/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ class KeyBinding(TypedDict):
'help_text': 'Show/hide message sender information',
'key_category': 'msg_actions',
},
'SHOW_POLL_VOTES': {
'keys': ['v'],
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure what key to suggest here, but v is already used by the web app for one feature, and ourselves in the message popup for another, so I'd prefer a different one.

One slightly different alternative would be to have it come from the message popup initially, which wouldn't be as intrusive, and we could use something like w, for showing information and interacting with widgets?

I was holding off of w otherwise, since it's the user search hotkey, and with modifiers might be a useful way of making those searches 'sticky' or not, per another issue.

'help_text': 'Show/hide poll voter list',
'key_category': 'msg_actions',
},
'EDIT_HISTORY': {
'keys': ['e'],
'help_text': 'Show/hide edit history',
Expand Down
29 changes: 29 additions & 0 deletions zulipterminal/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
MarkdownHelpView,
MsgInfoView,
NoticeView,
PollResultsView,
PopUpConfirmationView,
StreamInfoView,
StreamMembersView,
Expand Down Expand Up @@ -281,6 +282,34 @@ def show_msg_info(
)
self.show_pop_up(msg_info_view, "area:msg")

def show_poll_vote(
self,
poll_question: str,
options: Dict[str, Dict[str, Any]],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Assuming this is poll_options, this type is different again?

) -> None:
options_with_names = {}
for option_key, option_data in options.items():
option_text = option_data["option"]
voter_ids = option_data["votes"]

voter_names = []
for voter_id in voter_ids:
voter_names.append(self.model.user_name_from_id(voter_id))

options_with_names[option_key] = {
"option": option_text,
"votes": voter_names if voter_names else [],
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: Is this conditional necessary?

}

self.show_pop_up(
PollResultsView(
self,
poll_question,
neiljp marked this conversation as resolved.
Show resolved Hide resolved
options_with_names,
),
"area:msg",
)

def show_emoji_picker(self, message: Message) -> None:
all_emoji_units = [
(emoji_name, emoji["code"], emoji["aliases"])
Expand Down
Loading
Loading