-
-
Notifications
You must be signed in to change notification settings - Fork 278
Handle (un)subscribe events #1387
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
base: main
Are you sure you want to change the base?
Conversation
Hello @theViz343, it seems like you have referenced #816 in your pull request description, but you have not referenced them in your commit message description(s). Referencing an issue in a commit message automatically closes the corresponding issue when the commit is merged, which makes the issue tracker easier to manage. Please run An example of a correctly-formatted commit:
To learn how to write a great commit message, please refer to our guide. |
f3ed55e
to
2399169
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.
@theViz343 Thanks for the update on this - there's a lot to process here!
I've left a lot of feedback, but it is often contextual as I was reading through each commit, and in many cases overlaps with other comments to suggest an overall direction to take.
Separate from the specific code suggestions, quite a few of the other comments relate to structure between commits, and adjusting that will make further feedback easier. We can discuss commit order in the stream if that would help.
One additional factor that we may need to handle is unreads, which wasn't handled in the previous PR? I suspect unsubscribing should 'remove' any unreads in a stream, eg. they shouldn't contribute to the all-messages count. However, I don't know whether that data is explicitly cleared on the server side - do we get a mark-read event of some kind? - or whether clients are simply expected to zero any unreads in unsubscribed streams, or simply hide any unreads related to was-subscribed streams, ie. where unreads data is available but we are not now subscribed.
zulipterminal/model.py
Outdated
**self.stream_dict, | ||
**self.unsubscribed_stream_dict, | ||
**self.never_subscribed_stream_dict, |
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'm not sure if we'd keep this function as it is in light of my other comments on this commit, but I suspect the typing illustrates this is problematic if returned together, and could be so even with other lookup? Or is this mitigated if we type in such a way that a Subscription is-a Stream?
A more involved but possibly cleaner approach to consider later may be to store all the Stream data separate to the extra Subscription data, ie. use ZT-specific data structure for the personal data, though we'd have to do additional data processing for that 🤷 The advantage would be to have a cache of all streams, and cache of personal data per streams (now-subscribed and was-subscribed), which could make lookup easier.
zulipterminal/model.py
Outdated
def get_subscribed_streams(self) -> Dict[int, Any]: | ||
return self.stream_dict |
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.
You have a sequence of commits for the refactoring right now, but I'd suggest grouping the refactoring per each new function you introduce, concluding with a rename of stream_dict
:) I'd be keen to merge those commits as they are first, if you keep them together early in the branch (or split into another PR), since this encapsulation, renaming and stream-vs-subscription clarification should improve the code quality independently of the event/UI changes.
For this particular function I'd question its worth - it returns a mutable structure which is not typed precisely.
Instead I'd explore specific properties that are accessed at each lookup into stream_dict, and what is minimally necessary. For example, perhaps returning individual properties would be sufficient, eg. stream_property(some_id, property)
and similarly subscription_property(...)
, potentially limiting the properties to a subset of values until we add more features (via Literal types and/or at runtime). In most/all cases that should limit accidental mutability.
I'm not sure how many locations look up multiple stream/subscription properties, but I'd expect something like the above should be sufficient - I don't expect the multiple-property case to be overly frequent to motivate a more complex solution.
(On a similar note, once this is complete, we could consider improving how the pinned/unpinned data is handled, since it is something of a cached structure for multiple properties)
zulipterminal/api_types.py
Outdated
stream_weekly_traffic: Optional[int] | ||
|
||
|
||
class NeverSubscribedStream(TypedDict): |
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's likely clearer if we simply term this a Stream
, with a Subscription
being an extended form of it.
zulipterminal/api_types.py
Outdated
@@ -237,6 +237,56 @@ class Subscription(TypedDict): | |||
# in_home_view: bool # Replaced by is_muted in Zulip 2.1; still present in updates | |||
|
|||
|
|||
class UnsubscribedStream(TypedDict): |
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 Unsubscribed form appears to be identical to a Subscription
?
zulipterminal/model.py
Outdated
def get_stream_from_id( | ||
self, stream_id: int | ||
) -> Union[Subscription, UnsubscribedStream, NeverSubscribedStream]: |
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 not returning a 'stream', it's returning a stream or a subscription, as per the type :)
There may be cases where this is warranted, but I suspect the calling site either is interested in knowing subscription information, or general stream information - so I'd suggest we likely want dedicated methods for each. For example a stream doesn't have a color property, so should error on that request (and ideally fail via typing too), and each set of methods will have different bounds on valid stream ids.
zulipterminal/api_types.py
Outdated
@@ -461,6 +461,11 @@ class RealmUserEvent(TypedDict): | |||
# (also -peer_add and -peer_remove; FIXME: -add & -remove are not yet supported) | |||
|
|||
|
|||
class RemovedSubscription(TypedDict): | |||
stream_id: int | |||
name: str |
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: We need to be more consistent about this, but we could leave name commented out since we don't use it, with a comment to that effect?
zulipterminal/model.py
Outdated
stream_id = subscription["stream_id"] | ||
if stream_id in self.unsubscribed_stream_dict: | ||
del self.unsubscribed_stream_dict[stream_id] | ||
elif stream_id in self.never_subscribed_stream_dict: | ||
del self.never_subscribed_stream_dict[stream_id] | ||
self.stream_dict[stream_id] = subscription |
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: since the original line is expanded into a 'synchronize subscriptions' block, it seems clearer to have the stream_id extracted earlier?
That could be achieved by simply separating by a blank line, but the rest of this function could benefit from the use of stream_id
in any case?
zulipterminal/model.py
Outdated
if stream_id in self.unsubscribed_stream_dict: | ||
del self.unsubscribed_stream_dict[stream_id] | ||
elif stream_id in self.never_subscribed_stream_dict: | ||
del self.never_subscribed_stream_dict[stream_id] | ||
self.stream_dict[stream_id] = subscription |
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 understand that this is aimed at ensuring that the stream 'dicts' are synchronized, and corresponds to the code in the other function, but it seems strange included within the subscription-removal commit, since nothing is calling them except the initial call to this function, when the synchronization should be a no-op.
It may be clearer to consider adding and removing subscriptions separately, commit-wise?
zulipterminal/model.py
Outdated
if stream_id in self.stream_dict: | ||
self.unsubscribed_stream_dict[stream_id] = self.stream_dict[stream_id] | ||
del self.stream_dict[stream_id] |
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.
Readability: for comparison with the subscription function, this should maybe go at the top of the loop?
zulipterminal/model.py
Outdated
self.normalize_and_cache_message_retention_text() | ||
self.normalize_date_created_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.
These two lines here suggest these calls may belong in _subscribe_to_streams?
This commit creates data structures, similar to stream_dict, to support unsubscribed and never-subscribed streams. These streams are available in the fetched initial data which is used to populate the new data structures using the _register_non_subscribed_streams function.
This commit adds two accessor methods - _get_stream_from_id and get_all_stream_ids. These two accessor methods help in preparation for including specific stream property accessor methods in the next set of commits. Tests added.
This commit replaces the usage of directly indexing stream_dict for stream "name" with the new stream property accessor method "get_stream_name". Test added.
…ict. This commit replaces the usage of directly indexing stream_dict for "subscribers" with the new stream property accessor method "get_stream_subscribers". Test added.
This commit replaces the usage of directly indexing stream_dict for "date_created" data with the new stream property accessor methods "get_stream_date_created" and "set_stream_date_created". Tests added.
This commit replaces the usage of directly indexing stream_dict for "is_web_public" data with the new stream property accessor method "is_stream_web_public". Test added.
This commit replaces the usage of directly indexing stream_dict for "message_retention_days" data with the new stream property accessor methods "get_stream_message_retention_days" and "set_stream_message_retention_days". Test added.
This commit replaces the usage of directly indexing stream_dict for "is_invite_only" data with the new stream property accessor method "is_stream_invite_only". Test added.
This commit replaces the usage of directly indexing stream_dict for "stream_post_policy" with the new stream property accessor method "get_stream_post_policy". Test added.
This commit replaces the usage of directly indexing stream_dict for "is_announcement_only" data with the new stream property accessor method "is_stream_announcement_only". Test added.
…thod. This commit replaces the usage of directly indexing stream_dict for "history_public_to_subscribers" data with the new stream property accessor method "is_stream_history_public_to_subscribers". Test added.
This commit replaces the usage of directly indexing stream_dict for "stream_weekly_traffic" data with the new stream property accessor method "get_stream_weekly_traffic". Test added.
This commit replaces the usage of directly indexing stream_dict for "rendered_description" data with the new stream property accessor method "get_stream_rendered_description". Test added.
This commit adds the subscription ids accessor method "get_all_stream_ids". This helps in preparation for including subscription color accessor method added in the next commit. Tests added.
This commit replaces the usage of directly indexing stream_dict for "color" data with the new stream property accessor method "get_subscription_color". Test added.
…ict. This commit replaces the usage of directly indexing stream_dict for "email_address" data with the new stream property accessor method "get_subscription_email". Test added.
This commit improves the typing of stream_dict by using Subscription TypedDict instead of Dict[str, Any]. Tests updated.
This commit renames stream_dict to _subscribed_streams. This change is necessary due to the introduction of _unsubscribed_streams and never_subscribed streams in the model. Tests updated.
2399169
to
ab97a94
Compare
This commit moves the date_created field normalization code to a separate class function called normalize_date_created_field. The function adds the date_created field for a stream when the server feature level is less than 30. The value assigned is 'None'. Test added.
ab97a94
to
cd27476
Compare
The refactor introduced in earlier commits now allows stream data to be removed from non-subscribed stream data structures, when a stream is subscribed to. Test updated.
This commit adds a function named "_unsubscribe_from_streams" to model.py. This function updates subscription data by removing unsubscribed streams from class variables - pinned_streams, unpinned_streams, muted_streams, and visual_notified_streams. Co-authored-by: Abhirup Pal <abhiruppalmethodist@gmail.com> Co-authored-by: Vishwesh Pillai <vishwesh103@gmail.com>
This commit adds event handling code for newly added/removed subscriptions. To store the stream subscriptions which are being updated in the event, a new field named "subscriptions" is added to the SubscriptionEvent class. Co-authored-by: Abhirup Pal <abhiruppalmethodist@gmail.com> Co-authored-by: Vishwesh Pillai <vishwesh103@gmail.com>
cd27476
to
03aedca
Compare
Heads up @theViz343, we just merged some commits that conflict with the changes you made in this pull request! You can review this repository's recent commits to see where the conflicts occur. Please rebase your feature branch against the |
What does this PR do, and why?
This PR adds event handling for (un)subscribe events. Without event handling, ZT crashes
if the user narrows onto a message in a stream which is unsubscribed from. This happens mostly
when a user is logged in from multiple clients, but can also occur if an admin unsubscribes the user.
Outstanding aspect(s)
ZT not loading the colors.)
External discussion & connections
Adding streams throws key error
How did you test this?
Self-review checklist for each commit
Visual changes