mirror of https://github.com/zulip/zulip.git
popovers.js: Add version to user avatar request.
When a user changes its avatar image, the user's avatar in popovers wasn't being correctly updated, because of browser caching of the avatar image. We added a version on the request to get the image in the same format we use elsewhere, so the browser knows when to use the cached image or to make a new request to the server. Edited by Tim to preserve/fix sort orders in some tests, and update zulip_feature_level. Fixes: #14290
This commit is contained in:
parent
9137726e38
commit
7e9024a39c
|
@ -50,6 +50,7 @@ const alice = {
|
||||||
email: 'alice@example.com',
|
email: 'alice@example.com',
|
||||||
full_name: 'Alice Smith',
|
full_name: 'Alice Smith',
|
||||||
user_id: 42,
|
user_id: 42,
|
||||||
|
avatar_version: 5,
|
||||||
is_guest: false,
|
is_guest: false,
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
};
|
};
|
||||||
|
@ -184,13 +185,16 @@ run_test('sender_hover', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.user_popover_email').each = noop;
|
$('.user_popover_email').each = noop;
|
||||||
|
|
||||||
const image_stubber = make_image_stubber();
|
const image_stubber = make_image_stubber();
|
||||||
|
window.location = {
|
||||||
|
href: 'http://chat.zulip.org/',
|
||||||
|
};
|
||||||
|
const base_url = window.location.href;
|
||||||
handler.call(target, e);
|
handler.call(target, e);
|
||||||
|
|
||||||
const avatar_img = image_stubber.get(0);
|
const avatar_img = image_stubber.get(0);
|
||||||
assert.equal(avatar_img.src, 'avatar/42/medium');
|
const expected_url = new URL('avatar/42/medium?v=' + alice.avatar_version, base_url);
|
||||||
|
assert.equal(avatar_img.src.toString(), expected_url.toString());
|
||||||
|
|
||||||
// todo: load image
|
// todo: load image
|
||||||
});
|
});
|
||||||
|
|
|
@ -94,8 +94,10 @@ function init_email_clipboard() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function load_medium_avatar(user, elt) {
|
function load_medium_avatar(user, elt) {
|
||||||
const user_avatar_url = "avatar/" + user.user_id + "/medium";
|
const avatar_path = "avatar/" + user.user_id + "/medium?v=" + user.avatar_version;
|
||||||
|
const user_avatar_url = new URL(avatar_path, window.location.href);
|
||||||
const sender_avatar_medium = new Image();
|
const sender_avatar_medium = new Image();
|
||||||
|
|
||||||
sender_avatar_medium.src = user_avatar_url;
|
sender_avatar_medium.src = user_avatar_url;
|
||||||
$(sender_avatar_medium).on("load", function () {
|
$(sender_avatar_medium).on("load", function () {
|
||||||
elt.css("background-image", "url(" + $(this).attr("src") + ")");
|
elt.css("background-image", "url(" + $(this).attr("src") + ")");
|
||||||
|
|
|
@ -69,6 +69,7 @@ exports.update_person = function update(person) {
|
||||||
if (Object.prototype.hasOwnProperty.call(person, 'avatar_url')) {
|
if (Object.prototype.hasOwnProperty.call(person, 'avatar_url')) {
|
||||||
const url = person.avatar_url;
|
const url = person.avatar_url;
|
||||||
person_obj.avatar_url = url;
|
person_obj.avatar_url = url;
|
||||||
|
person_obj.avatar_version = person.avatar_version;
|
||||||
|
|
||||||
if (people.is_my_user_id(person.user_id)) {
|
if (people.is_my_user_id(person.user_id)) {
|
||||||
page_params.avatar_source = person.avatar_source;
|
page_params.avatar_source = person.avatar_source;
|
||||||
|
|
|
@ -10,6 +10,15 @@ below features are supported.
|
||||||
|
|
||||||
## Changes in Zulip 2.2
|
## Changes in Zulip 2.2
|
||||||
|
|
||||||
|
**Feature level 6**
|
||||||
|
* [`GET /events`](/api/get-events-from-queue): `realm_user` events to
|
||||||
|
update a user's avatar now include the `avatar_version` field, which
|
||||||
|
is important for correctly refetching medium-size avatar images when
|
||||||
|
the user's avatar changes.
|
||||||
|
* [`GET /users`](/api/get-all-users) and [`GET
|
||||||
|
/users/{user_id}`](/api/get-user): User objects now contain the
|
||||||
|
`avatar_version` field as well.
|
||||||
|
|
||||||
**Feature level 5**
|
**Feature level 5**
|
||||||
* [`GET /events`](/api/get-events-from-queue): `realm_bot` events,
|
* [`GET /events`](/api/get-events-from-queue): `realm_bot` events,
|
||||||
sent when changes are made to bot users, now contain an
|
sent when changes are made to bot users, now contain an
|
||||||
|
|
|
@ -29,7 +29,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
|
||||||
#
|
#
|
||||||
# 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.
|
# new level means in templates/zerver/api/changelog.md.
|
||||||
API_FEATURE_LEVEL = 5
|
API_FEATURE_LEVEL = 6
|
||||||
|
|
||||||
# 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
|
||||||
|
|
|
@ -3263,10 +3263,11 @@ def notify_avatar_url_change(user_profile: UserProfile) -> None:
|
||||||
bot_owner_user_ids(user_profile))
|
bot_owner_user_ids(user_profile))
|
||||||
|
|
||||||
payload = dict(
|
payload = dict(
|
||||||
email=user_profile.email,
|
|
||||||
avatar_source=user_profile.avatar_source,
|
avatar_source=user_profile.avatar_source,
|
||||||
avatar_url=avatar_url(user_profile),
|
avatar_url=avatar_url(user_profile),
|
||||||
avatar_url_medium=avatar_url(user_profile, medium=True),
|
avatar_url_medium=avatar_url(user_profile, medium=True),
|
||||||
|
avatar_version=user_profile.avatar_version,
|
||||||
|
email=user_profile.email,
|
||||||
user_id=user_profile.id
|
user_id=user_profile.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -315,6 +315,7 @@ def format_user_row(realm: Realm, acting_user: UserProfile, row: Dict[str, Any],
|
||||||
email=row['email'],
|
email=row['email'],
|
||||||
user_id=row['id'],
|
user_id=row['id'],
|
||||||
avatar_url=avatar_url,
|
avatar_url=avatar_url,
|
||||||
|
avatar_version=row['avatar_version'],
|
||||||
is_admin=is_admin,
|
is_admin=is_admin,
|
||||||
is_guest=is_guest,
|
is_guest=is_guest,
|
||||||
is_bot=is_bot,
|
is_bot=is_bot,
|
||||||
|
|
|
@ -672,14 +672,14 @@ class ListCustomProfileFieldTest(CustomProfileFieldTestCase):
|
||||||
|
|
||||||
expected_keys_for_iago = {
|
expected_keys_for_iago = {
|
||||||
"delivery_email",
|
"delivery_email",
|
||||||
"email", "user_id", "avatar_url", "is_admin", "is_guest", "is_bot",
|
"email", "user_id", "avatar_url", "avatar_version", "is_admin", "is_guest", "is_bot",
|
||||||
"full_name", "timezone", "is_active", "date_joined", "profile_data"}
|
"full_name", "timezone", "is_active", "date_joined", "profile_data"}
|
||||||
self.assertEqual(set(iago_raw_data.keys()), expected_keys_for_iago)
|
self.assertEqual(set(iago_raw_data.keys()), expected_keys_for_iago)
|
||||||
self.assertNotEqual(iago_raw_data["profile_data"], {})
|
self.assertNotEqual(iago_raw_data["profile_data"], {})
|
||||||
|
|
||||||
expected_keys_for_test_bot = {
|
expected_keys_for_test_bot = {
|
||||||
"delivery_email",
|
"delivery_email",
|
||||||
"email", "user_id", "avatar_url", "is_admin", "is_guest", "is_bot", "full_name",
|
"email", "user_id", "avatar_url", "avatar_version", "is_admin", "is_guest", "is_bot", "full_name",
|
||||||
"timezone", "is_active", "date_joined", "bot_type", "bot_owner_id"}
|
"timezone", "is_active", "date_joined", "bot_type", "bot_owner_id"}
|
||||||
self.assertEqual(set(test_bot_raw_data.keys()), expected_keys_for_test_bot)
|
self.assertEqual(set(test_bot_raw_data.keys()), expected_keys_for_test_bot)
|
||||||
self.assertEqual(test_bot_raw_data["bot_type"], 1)
|
self.assertEqual(test_bot_raw_data["bot_type"], 1)
|
||||||
|
|
|
@ -1247,6 +1247,7 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
('user_id', check_int),
|
('user_id', check_int),
|
||||||
('email', check_string),
|
('email', check_string),
|
||||||
('avatar_url', check_none_or(check_string)),
|
('avatar_url', check_none_or(check_string)),
|
||||||
|
('avatar_version', check_int),
|
||||||
('full_name', check_string),
|
('full_name', check_string),
|
||||||
('is_admin', check_bool),
|
('is_admin', check_bool),
|
||||||
('is_bot', check_bool),
|
('is_bot', check_bool),
|
||||||
|
@ -1273,6 +1274,7 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
('user_id', check_int),
|
('user_id', check_int),
|
||||||
('email', check_string),
|
('email', check_string),
|
||||||
('avatar_url', check_none_or(check_string)),
|
('avatar_url', check_none_or(check_string)),
|
||||||
|
('avatar_version', check_int),
|
||||||
('full_name', check_string),
|
('full_name', check_string),
|
||||||
('is_active', check_bool),
|
('is_active', check_bool),
|
||||||
('is_admin', check_bool),
|
('is_admin', check_bool),
|
||||||
|
@ -1534,6 +1536,7 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
('user_id', check_int),
|
('user_id', check_int),
|
||||||
('avatar_url', check_string),
|
('avatar_url', check_string),
|
||||||
('avatar_url_medium', check_string),
|
('avatar_url_medium', check_string),
|
||||||
|
('avatar_version', check_int),
|
||||||
('avatar_source', check_string),
|
('avatar_source', check_string),
|
||||||
])),
|
])),
|
||||||
])
|
])
|
||||||
|
@ -1547,11 +1550,12 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
('type', equals('realm_user')),
|
('type', equals('realm_user')),
|
||||||
('op', equals('update')),
|
('op', equals('update')),
|
||||||
('person', check_dict_only([
|
('person', check_dict_only([
|
||||||
('email', check_string),
|
('avatar_source', check_string),
|
||||||
('user_id', check_int),
|
|
||||||
('avatar_url', check_none_or(check_string)),
|
('avatar_url', check_none_or(check_string)),
|
||||||
('avatar_url_medium', check_none_or(check_string)),
|
('avatar_url_medium', check_none_or(check_string)),
|
||||||
('avatar_source', check_string),
|
('avatar_version', check_int),
|
||||||
|
('email', check_string),
|
||||||
|
('user_id', check_int),
|
||||||
])),
|
])),
|
||||||
])
|
])
|
||||||
events = self.do_test(
|
events = self.do_test(
|
||||||
|
@ -1589,9 +1593,10 @@ class EventsRegisterTest(ZulipTestCase):
|
||||||
('person', check_dict_only([
|
('person', check_dict_only([
|
||||||
('email', check_string),
|
('email', check_string),
|
||||||
('user_id', check_int),
|
('user_id', check_int),
|
||||||
|
('avatar_source', check_string),
|
||||||
('avatar_url', check_string),
|
('avatar_url', check_string),
|
||||||
('avatar_url_medium', check_string),
|
('avatar_url_medium', check_string),
|
||||||
('avatar_source', check_string),
|
('avatar_version', check_int),
|
||||||
])),
|
])),
|
||||||
])
|
])
|
||||||
do_set_realm_property(self.user_profile.realm, "email_address_visibility",
|
do_set_realm_property(self.user_profile.realm, "email_address_visibility",
|
||||||
|
|
|
@ -604,6 +604,7 @@ class HomeTest(ZulipTestCase):
|
||||||
|
|
||||||
self.assertEqual(sorted(cross_bots, key=by_email), sorted([
|
self.assertEqual(sorted(cross_bots, key=by_email), sorted([
|
||||||
dict(
|
dict(
|
||||||
|
avatar_version=email_gateway_bot.avatar_version,
|
||||||
bot_owner_id=None,
|
bot_owner_id=None,
|
||||||
bot_type=1,
|
bot_type=1,
|
||||||
email=email_gateway_bot.email,
|
email=email_gateway_bot.email,
|
||||||
|
@ -616,6 +617,7 @@ class HomeTest(ZulipTestCase):
|
||||||
is_guest=False
|
is_guest=False
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
|
avatar_version=email_gateway_bot.avatar_version,
|
||||||
bot_owner_id=None,
|
bot_owner_id=None,
|
||||||
bot_type=1,
|
bot_type=1,
|
||||||
email=notification_bot.email,
|
email=notification_bot.email,
|
||||||
|
@ -628,6 +630,7 @@ class HomeTest(ZulipTestCase):
|
||||||
is_guest=False
|
is_guest=False
|
||||||
),
|
),
|
||||||
dict(
|
dict(
|
||||||
|
avatar_version=email_gateway_bot.avatar_version,
|
||||||
bot_owner_id=None,
|
bot_owner_id=None,
|
||||||
bot_type=1,
|
bot_type=1,
|
||||||
email=welcome_bot.email,
|
email=welcome_bot.email,
|
||||||
|
|
Loading…
Reference in New Issue