subscriptions: Send update events for `is_muted` property.

In Zulip 2.1.0, the `is_muted` stream subscription property was
added and replaced the `in_home_view` property. But the server has
still only been sending subscription update events with the
`in_home_view` property.

Updates `do_change_subscription_property` to send a subscription
update event for both `is_muted` and `in_home_view`, so that
clients can fully migrate away from using `in_home_view` allowing
us to eventually remove it completely.
This commit is contained in:
Lauryn Menard 2022-08-09 20:37:07 +02:00 committed by Tim Abbott
parent 47e5ccf086
commit 885c3d6593
6 changed files with 109 additions and 21 deletions

View File

@ -20,6 +20,15 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 6.0 ## Changes in Zulip 6.0
**Feature level 139**
* [`GET /get-events`](/api/get-events): When a user mutes or unmutes
their subscription to a stream, a `subscription` update event
is now sent for the `is_muted` property and for the deprecated
`in_home_view` property to support clients fully migrating to use the
`is_muted` property. Prior to this feature level, only one event was
sent to clients with the deprecated `in_home_view` property.
**Feature level 138** **Feature level 138**
* [`POST /register`](/api/register-queue), [`GET * [`POST /register`](/api/register-queue), [`GET
@ -228,7 +237,7 @@ No changes; feature level used for Zulip 5.0 release.
**Feature level 111** **Feature level 111**
* [`POST /subscriptions/properties`](/api/update-subscription-settings): * [`POST /users/me/subscriptions/properties`](/api/update-subscription-settings):
Removed `subscription_data` from response object, replacing it with Removed `subscription_data` from response object, replacing it with
`ignored_parameters_unsupported`. `ignored_parameters_unsupported`.
@ -1137,7 +1146,8 @@ No changes; feature level used for Zulip 3.0 release.
`demote_inactive_streams` display settings. `demote_inactive_streams` display settings.
* `enable_stream_sounds` was renamed to * `enable_stream_sounds` was renamed to
`enable_stream_audible_notifications`. `enable_stream_audible_notifications`.
* Deprecated `in_home_view`, replacing it with the more readable * [`POST /users/me/subscriptions/properties`](/api/update-subscription-settings):
Deprecated `in_home_view`, replacing it with the more readable
`is_muted` (with the opposite meaning). `is_muted` (with the opposite meaning).
* Custom profile fields: Added `EXTERNAL_ACCOUNT` field type. * Custom profile fields: Added `EXTERNAL_ACCOUNT` field type.

View File

@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
# Changes should be accompanied by documentation explaining what the # Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md, as well as # new level means in templates/zerver/api/changelog.md, as well as
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`. # "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 138 API_FEATURE_LEVEL = 139
# Bump the minor PROVISION_VERSION to indicate that folks should provision # Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump # only when going from an old version of the code to a newer version. Bump

View File

@ -769,19 +769,14 @@ def do_change_subscription_property(
acting_user: Optional[UserProfile], acting_user: Optional[UserProfile],
) -> None: ) -> None:
database_property_name = property_name database_property_name = property_name
event_property_name = property_name
database_value = value database_value = value
event_value = value
# For this property, is_muted is used in the database, but # For this property, is_muted is used in the database, but
# in_home_view in the API, since we haven't migrated the events # in_home_view is still in the API, since we haven't fully
# API to the new name yet. # migrated to the new name yet.
if property_name == "in_home_view": if property_name == "in_home_view":
database_property_name = "is_muted" database_property_name = "is_muted"
database_value = not value database_value = not value
if property_name == "is_muted":
event_property_name = "in_home_view"
event_value = not value
old_value = getattr(sub, database_property_name) old_value = getattr(sub, database_property_name)
setattr(sub, database_property_name, database_value) setattr(sub, database_property_name, database_value)
@ -803,11 +798,26 @@ def do_change_subscription_property(
).decode(), ).decode(),
) )
# This first in_home_view event is deprecated and will be removed
# once clients are migrated to handle the subscription update event
# with is_muted as the property name.
if database_property_name == "is_muted":
event_value = not database_value
in_home_view_event = dict(
type="subscription",
op="update",
property="in_home_view",
value=event_value,
stream_id=stream.id,
)
send_event(user_profile.realm, in_home_view_event, [user_profile.id])
event = dict( event = dict(
type="subscription", type="subscription",
op="update", op="update",
property=event_property_name, property=database_property_name,
value=event_value, value=database_value,
stream_id=stream.id, stream_id=stream.id,
) )
send_event(user_profile.realm, event, [user_profile.id]) send_event(user_profile.realm, event, [user_profile.id])

View File

@ -697,8 +697,9 @@ paths:
description: | description: |
Event sent to a user's clients when a property of the user's Event sent to a user's clients when a property of the user's
subscription to a stream has been updated. This event is used subscription to a stream has been updated. This event is used
only for personal properties like `is_muted`; see the `stream` event only for personal properties like `is_muted` or `pin_to_top`.
for global properties of a stream. See the [stream update event](/api/get-events#stream-update)
for updates to global properties of a stream.
properties: properties:
id: id:
$ref: "#/components/schemas/EventIdSchema" $ref: "#/components/schemas/EventIdSchema"
@ -718,13 +719,19 @@ paths:
property: property:
type: string type: string
description: | description: |
The property of the subscription which has changed. See The property of the subscription which has changed. For details on the
[/users/me/subscriptions/properties GET](/api/update-subscription-settings) various subscription properties that a user can change, see
for details on the various properties of a stream. [POST /users/me/subscriptions/properties](/api/update-subscription-settings).
Clients should generally handle an unknown property received here without Clients should generally handle an unknown property received here without
crashing, since that will naturally happen when connecting to a Zulip crashing, since that will naturally happen when connecting to a Zulip
server running a new version that adds a new subscription property. server running a new version that adds a new subscription property.
**Changes**: As of Zulip 6.0 (feature level 139), updates to the `is_muted`
property or the deprecated `in_home_view` property will send two `subscription`
update events, one for each property, to support clients fully migrating to
use the `is_muted` property. Prior to this feature level, updates to either
property only sent one event with the deprecated `in_home_view` property.
value: value:
description: | description: |
The new value of the changed property. The new value of the changed property.
@ -8773,8 +8780,14 @@ paths:
- `"color"`: The hex value of the user's display color for the stream. - `"color"`: The hex value of the user's display color for the stream.
- `"is_muted"`: Whether the stream is [muted](/help/mute-a-stream).<br> - `"is_muted"`: Whether the stream is [muted](/help/mute-a-stream).<br>
**Changes**: As of Zulip 6.0 (feature level 139), updating either
`"is_muted"` or `"in_home_view"` generates two [subscription update
events](/api/get-events#subscription-update), one for each property,
that are sent to clients. Prior to this feature level, updating either
property only generated a subscription update event for
`"in_home_view"`. <br>
**Changes**: Prior to Zulip 2.1.0, this feature was represented **Changes**: Prior to Zulip 2.1.0, this feature was represented
by the more confusingly named `in_home_view` (with the by the more confusingly named `"in_home_view"` (with the
opposite value: `in_home_view=!is_muted`); for opposite value: `in_home_view=!is_muted`); for
backwards-compatibility, modern Zulip still accepts that property. backwards-compatibility, modern Zulip still accepts that property.

View File

@ -1481,6 +1481,55 @@ class NormalActionsTest(BaseAction):
value=pinned, value=pinned,
) )
def test_mute_and_unmute_stream(self) -> None:
stream = get_stream("Denmark", self.user_profile.realm)
sub = get_subscription(stream.name, self.user_profile)
# While migrating events API from in_home_view to is_muted:
# First, test in_home_view sends 2 events: in_home_view and is_muted.
do_change_subscription_property(
self.user_profile, sub, stream, "in_home_view", False, acting_user=None
)
events = self.verify_action(
lambda: do_change_subscription_property(
self.user_profile, sub, stream, "in_home_view", True, acting_user=None
),
num_events=2,
)
check_subscription_update(
"events[0]",
events[0],
property="in_home_view",
value=True,
)
check_subscription_update(
"events[1]",
events[1],
property="is_muted",
value=False,
)
# Then, test is_muted also sends both events, in the same order.
events = self.verify_action(
lambda: do_change_subscription_property(
self.user_profile, sub, stream, "is_muted", True, acting_user=None
),
num_events=2,
)
check_subscription_update(
"events[0]",
events[0],
property="in_home_view",
value=False,
)
check_subscription_update(
"events[1]",
events[1],
property="is_muted",
value=True,
)
def test_change_stream_notification_settings(self) -> None: def test_change_stream_notification_settings(self) -> None:
for setting_name in ["email_notifications"]: for setting_name in ["email_notifications"]:
stream = get_stream("Denmark", self.user_profile.realm) stream = get_stream("Denmark", self.user_profile.realm)

View File

@ -3016,7 +3016,7 @@ class SubscriptionPropertiesTest(ZulipTestCase):
events: List[Mapping[str, Any]] = [] events: List[Mapping[str, Any]] = []
property_name = "is_muted" property_name = "is_muted"
with self.tornado_redirected_to_list(events, expected_num_events=1): with self.tornado_redirected_to_list(events, expected_num_events=2):
result = self.api_post( result = self.api_post(
test_user, test_user,
"/api/v1/users/me/subscriptions/properties", "/api/v1/users/me/subscriptions/properties",
@ -3035,6 +3035,8 @@ class SubscriptionPropertiesTest(ZulipTestCase):
self.assert_json_success(result) self.assert_json_success(result)
self.assertEqual(events[0]["event"]["property"], "in_home_view") self.assertEqual(events[0]["event"]["property"], "in_home_view")
self.assertEqual(events[0]["event"]["value"], False) self.assertEqual(events[0]["event"]["value"], False)
self.assertEqual(events[1]["event"]["property"], "is_muted")
self.assertEqual(events[1]["event"]["value"], True)
sub = Subscription.objects.get( sub = Subscription.objects.get(
recipient__type=Recipient.STREAM, recipient__type=Recipient.STREAM,
recipient__type_id=subs[0]["stream_id"], recipient__type_id=subs[0]["stream_id"],
@ -3043,7 +3045,7 @@ class SubscriptionPropertiesTest(ZulipTestCase):
self.assertEqual(sub.is_muted, True) self.assertEqual(sub.is_muted, True)
legacy_property_name = "in_home_view" legacy_property_name = "in_home_view"
with self.tornado_redirected_to_list(events, expected_num_events=1): with self.tornado_redirected_to_list(events, expected_num_events=2):
result = self.api_post( result = self.api_post(
test_user, test_user,
"/api/v1/users/me/subscriptions/properties", "/api/v1/users/me/subscriptions/properties",
@ -3062,6 +3064,8 @@ class SubscriptionPropertiesTest(ZulipTestCase):
self.assert_json_success(result) self.assert_json_success(result)
self.assertEqual(events[0]["event"]["property"], "in_home_view") self.assertEqual(events[0]["event"]["property"], "in_home_view")
self.assertEqual(events[0]["event"]["value"], True) self.assertEqual(events[0]["event"]["value"], True)
self.assertEqual(events[1]["event"]["property"], "is_muted")
self.assertEqual(events[1]["event"]["value"], False)
self.assert_json_success(result) self.assert_json_success(result)
sub = Subscription.objects.get( sub = Subscription.objects.get(
recipient__type=Recipient.STREAM, recipient__type=Recipient.STREAM,
@ -3070,7 +3074,7 @@ class SubscriptionPropertiesTest(ZulipTestCase):
) )
self.assertEqual(sub.is_muted, False) self.assertEqual(sub.is_muted, False)
with self.tornado_redirected_to_list(events, expected_num_events=1): with self.tornado_redirected_to_list(events, expected_num_events=2):
result = self.api_post( result = self.api_post(
test_user, test_user,
"/api/v1/users/me/subscriptions/properties", "/api/v1/users/me/subscriptions/properties",
@ -3089,6 +3093,8 @@ class SubscriptionPropertiesTest(ZulipTestCase):
self.assert_json_success(result) self.assert_json_success(result)
self.assertEqual(events[0]["event"]["property"], "in_home_view") self.assertEqual(events[0]["event"]["property"], "in_home_view")
self.assertEqual(events[0]["event"]["value"], False) self.assertEqual(events[0]["event"]["value"], False)
self.assertEqual(events[1]["event"]["property"], "is_muted")
self.assertEqual(events[1]["event"]["value"], True)
sub = Subscription.objects.get( sub = Subscription.objects.get(
recipient__type=Recipient.STREAM, recipient__type=Recipient.STREAM,