-
-
Notifications
You must be signed in to change notification settings - Fork 259
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
Support (un)resolving topics #1544
base: main
Are you sure you want to change the base?
Conversation
fb82ade
to
a570d28
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.
@rsashank Thanks for the followup 👍
I may have had a few more thoughts, but wanted to get these comments to you before I go to bed :)
Re co-authoring, please note that
- Shivam's email doesn't appear to match his original commits
- some commits look entirely to be his with no additions; can you cherry-pick those from the original PR instead to indicate that?
initial_data[ | ||
"realm_community_topic_editing_limit_seconds" | ||
] = topic_editing_limit_seconds | ||
initial_data[ | ||
"realm_move_messages_within_stream_limit_seconds" | ||
] = move_messages_within_stream_limit_seconds |
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.
Strictly this doesn't cover the ZFL 183+ (Zulip 7) situation, where the first of these parameters was removed?
We could treat the two parameters as a patch to initial data via a dict, but unfortunately that then leaves assumptions as to whether the two values are in initial_data
, ie. for later versions we strictly want to remove the field.
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've added cases for ZFL184, where the value of realm_community_topic_editing_limit_seconds
is None.
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.
My point was that some fields are actually missing at some feature levels, not that they are None
, or I'd at least expect that at early server versions - and if an option is actually removed at a later version?
def test_toggle_resolve_status(self) -> None: | ||
resolve_button = self.topic_info_view.widgets[-1] | ||
resolve_button._emit("click") | ||
|
||
self.controller.model.toggle_topic_resolve_status.assert_called_once_with( | ||
stream_id=self.stream_id, topic_name=self.topic | ||
) | ||
|
||
self.controller.exit_popup.assert_called_once() |
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 looks like you added this extra test, and the one above this?
I'm not sure about using _emit
here, at least compared to our normal use of keypress
, but that should be fine - what inspired _emit
?
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 initially considered using keypress
, but assumed the Enter key essentially uses ACTIVATE which triggers a click event in the context of an Urwid Button, just a NIT but thought it would be straightforward to use _emit
.
I can change it back to use keypress
with ACTIVATE_BUTTON
Urwid Docs
Send ‘click’ signal on ‘activate’ command.
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 is fine, though I'll leave this comment unresolved since it may be useful to take this to a discussion of how best to integrate urwid and our own key handling further in this way.
zulipterminal/config/keys.py
Outdated
@@ -312,6 +312,11 @@ class KeyBinding(TypedDict): | |||
'help_text': 'Show/hide stream information & modify settings', | |||
'key_category': 'stream_list', | |||
}, | |||
'TOPIC_INFO': { | |||
'keys': ['i'], | |||
'help_text': 'Show/hide topic information & modify settings', |
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 is only useful for this feature right now, so perhaps we should update this slightly to mention updating the resolve status.
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.
Is it alright now?
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, that seems fine for now 👍
) | ||
|
||
self.widgets.append(resolve_topic_setting) | ||
super().__init__(controller, self.widgets, "TOPIC_INFO", popup_width, title) |
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 TOPIC_INFO has no meaning here yet. Not sure of the ordering; let's leave it for now.
7bea76f
to
02963cd
Compare
The function calls get_latest_message_in_topic to fetch recent message in topic to be (un)resolved. It verifies user and editing conditions using can_user_edit_topic function and finally add or remove RESOLVED_TOPIC_PREFIX from topic name. Co-authored-by: Shivam Deotarse <[email protected]>
Tests added. Co-authored-by: Shivam Deotarse <[email protected]>
Co-authored-by: Shivam Deotarse <[email protected]>
02963cd
to
955fb11
Compare
955fb11
to
76fafdf
Compare
@neiljp Thanks for the feedback! Updated this PR :) |
|
||
model.toggle_topic_resolve_status(stream_id, topic_name) | ||
|
||
report_error.assert_called_once_with(expected_footer_error) |
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.
For comparison with the previous test, we can also assert that the update method is not called, and vice versa in the other test.
I mentioned this point indirectly when the tests were combined - each branch can assert on each element.
tests/model/test_model.py
Outdated
model.get_latest_message_in_topic = mocker.Mock( | ||
return_value={ | ||
"subject": topic_name, | ||
"timestamp": timestamp, | ||
"id": 1, | ||
} | ||
) |
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.
Minor nits: if you have arbitrary constants, it's often useful to have them different, in case they 'work' when they are accidentally used in place of one another.
It's debatable whether a message id of 1 makes sense here, but it's a parameter so easily changed or parametrized later.
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.
@rsashank Thanks for the update 👍 My comments are mostly re tests and error handling, and many are hopefully small.
Please let me know if you have questions!
I did notice that, while I mentioned that Shivam's email for this was different, his merged commits look like another email address. This should be fine either way, as we can update the .mailmap for this if necessary.
), | ||
], | ||
) | ||
def test_toggle_topic_resolve_status_no_footer_error( |
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.
Nit: We've been switching towards a double underscore between the function name and the test meaning/motivation - it makes it clearer which part is which. (same elsewhere)
case( | ||
"hi!", | ||
0, | ||
12, | ||
86400, | ||
None, | ||
" Time limit for editing topic has been exceeded.", | ||
id="time_limit_exceeded:Zulip2.1+:ZFL12", | ||
), | ||
case( | ||
"hi!", | ||
0, | ||
162, | ||
259200, | ||
259200, | ||
" Time limit for editing topic has been exceeded.", | ||
id="time_limit_exceeded:Zulip7.0+:ZFL162", | ||
), | ||
case( | ||
"hi!", | ||
0, | ||
184, | ||
None, | ||
86400, | ||
" Time limit for editing topic has been exceeded.", | ||
id="topic_resolved:Zulip2.1+:ZFL184", |
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.
Looking at these cases alone, it's not clear to me what's different in each one?
- Except for the last case (with incorrect id?) the id starts with time_limit_exceeded; that's currently implied by the test, though might not be, if you extend the code to handle other errors.
- Does the topic name need to be parametrized? We're not testing unresolving as it stands, but could parametrize over that separately instead (using a nested decorator).
- Does the latest_message_timestamp matter? It appears to be zero in every case, which is constant among the cases (can it be a constant like message_id above?), but it's unclear what that value is trying to achieve?
- Is the error string a constant between each case? (again, it may not be if the error handling is extended)
latest_msg = self.get_latest_message_in_topic(stream_id, topic_name) | ||
if not self.can_user_edit_topic() or not latest_msg: | ||
return |
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.
These should report errors, based on how the function currently works - this was the error handling I mentioned was absent, and would be more obvious if restructured to be like this.
# ZFL < 11, community_topic_editing_limit_seconds | ||
# was hardcoded as int value in secs eg. 86400s (1 day) or None | ||
else: | ||
edit_time_limit = 86400 |
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 know this comment was moved, but it doesn't map to the value being None
?
My related question is whether edit_time_limit
can end up being None
for a well-specified server? It's checked against None
below, which might just be related to the .get
calls above, or if the 'seconds' values can actually have a None/null value that has a distinct meaning.
zulipterminal/config/keys.py
Outdated
@@ -312,6 +312,11 @@ class KeyBinding(TypedDict): | |||
'help_text': 'Show/hide stream information & modify settings', | |||
'key_category': 'stream_list', | |||
}, | |||
'TOPIC_INFO': { | |||
'keys': ['i'], | |||
'help_text': 'Show/hide topic information & modify settings', |
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, that seems fine for now 👍
"topic_name, expected_topic_button_label", | ||
[ | ||
("hi!", "Resolve Topic"), | ||
(f"{RESOLVED_TOPIC_PREFIX}" + "hi!", "Unresolve Topic"), |
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.
Nit: Unless you plan to extract the 'hi!', this can be a combined f-string without the addition.
def test_toggle_resolve_status(self) -> None: | ||
resolve_button = self.topic_info_view.widgets[-1] | ||
resolve_button._emit("click") | ||
|
||
self.controller.model.toggle_topic_resolve_status.assert_called_once_with( | ||
stream_id=self.stream_id, topic_name=self.topic | ||
) | ||
|
||
self.controller.exit_popup.assert_called_once() |
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 is fine, though I'll leave this comment unresolved since it may be useful to take this to a discussion of how best to integrate urwid and our own key handling further in this way.
'area:stream' : (Color.DARK0_HARD, Color.BRIGHT_BLUE), | ||
'area:topic' : (Color.DARK0_HARD, Color.BRIGHT_BLUE), |
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 is useful to have distinct styles to enable changing them later, but I'm wondering if we should use different colors, rather than simply having duplicates of area:stream
.
self.show_pop_up(show_stream_view, "area:stream") | ||
|
||
def show_topic_info(self, stream_id: int, topic_name: str) -> None: | ||
show_topic_view = TopicInfoView(self, stream_id, topic_name) | ||
self.show_pop_up(show_topic_view, "area:topic") | ||
|
||
def show_stream_members(self, stream_id: int) -> None: | ||
stream_members_view = StreamMembersView(self, stream_id) | ||
self.show_pop_up(stream_members_view, "area:stream") |
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.
Nit: This would be better not between two stream-related functions.
zulipterminal/model.py
Outdated
# ZFL < 11, community_topic_editing_limit_seconds | ||
# was hardcoded as int value in secs eg. 86400s (1 day) or None | ||
if 11 <= self.server_feature_level < 162: | ||
edit_time_limit = self.initial_data.get( | ||
"realm_community_topic_editing_limit_seconds", None | ||
) | ||
# ZFL >= 162, realm_move_messages_within_stream_limit_seconds was | ||
# introduced in place of realm_community_topic_editing_limit_seconds | ||
elif self.server_feature_level >= 162: | ||
edit_time_limit = self.initial_data.get( | ||
"realm_move_messages_within_stream_limit_seconds", None | ||
) | ||
else: | ||
edit_time_limit = 86400 |
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 is OK for now, though a TODO comment above the block of code would be useful.
Note that my comments re the tests could end up splitting the resolve/unresolve and version-dependent parts, at least in terms of parametrize, which is potentially part way towards the simplification I mentioned would be possible in my previous comment.
What does this PR do, and why?
This PR adds support for (un)resolving topics in ZT via TopicInfoView popup menu when topic is highlighted in left_stream_bar and i key is pressed to toggle topic settings.
Changes with respect to completion candidate PR:
Added support beyond ZFL 183:
Before Zulip 7.0 (feature level 183), the realm_community_topic_editing_limit_seconds property was returned by the response. It was removed because it had not been in use since the realm setting move_messages_within_stream_limit_seconds was introduced in feature level 162.
Changes to tests
Dropped the first commit
External discussion & connections
Support (un)resolving topics #T1235 (fix #T1075)
How did you test this?
Self-review checklist for each commit
Visual changes