-
-
Notifications
You must be signed in to change notification settings - Fork 651
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
Crunchify shell for avatar URLs, handle user_avatar_url_field_optional
client capability
#4230
Conversation
This was on my physical iPhone 6s device, on At the tip (edcf172): 4607.139892578125ms On 3194.426025390625ms |
b05f080
to
edcf172
Compare
src/__tests__/lib/exampleData.js
Outdated
import { ZulipVersion } from '../../utils/zulipVersion'; | ||
|
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.
Looks like an unintentional newline here; I'll fix this in my next revision.
edcf172
to
e265e34
Compare
84d2de8
to
57c3fdf
Compare
OK, this is no longer blocked by #4235, since it was just merged! I've also gone and updated this branch with those changes, so it's ready for review. And I should do another set of measurements as well. |
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.
Thanks @chrisbobbe !
Below is a half-done review on this which I apparently didn't send -- just now spotted it in a tab. (Thankfully GitHub does save "pending" review comments.)
IIRC I hadn't finished reading through the branch when I apparently got distracted and forgot about this review. Not sure how far through I'd read; I'll have to figure that out when I return to it to complete the review. But I think these comments will be enough to be helpful, so wanted to send them without further delay.
Like it already says in the detail about `user_avatar_url_field_optional`, but on the field itself [1]. [1] zulip/zulip-mobile#4230 (comment)
57c3fdf
to
1d89afc
Compare
Thanks for the review! I just pushed a new revision. |
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.
Thanks @chrisbobbe ! Now finished a review -- comments below.
src/lightbox/LightboxHeader.js
Outdated
size={36} | ||
// In the next commit, `UserAvatarWithPresence` will take an | ||
// AvatarURL instance for `avatarUrl`. | ||
avatarUrl={avatarUrl.get(36).toString()} |
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 36 here looks like it changes some behavior. Previously the avatarUrl
value here came from getAvatarFromMessage
at the caller, and that used the default size of 80.
I think perhaps the root cause of having this behavior change happen in the same commit with the big refactor is that there are two separate implementations of "compute the actual URL from the pattern the server provides in avatar_url
": there's the new classes with their methods, and there's the old helper functions like getAvatarFromMessage
.
Can we do an NFC commit before this one that turns the latter into thin wrappers around the former? I think that would help isolate any behavior changes, and help confirm we're not making any that are unintended.
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.
Can we do an NFC commit before this one that turns the latter into thin wrappers around the former?
Hmm, I'm probably missing something, so I'd better check. A commit that turns old helper functions into thin wrappers around new code doesn't sound NFC; is that right? In a commit like that, the old helper functions' old implementations would go from being used to not being used.
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.
Can we do an NFC commit before this one that turns the latter into thin wrappers around the former?
Hmm, I'm probably missing something, so I'd better check. A commit that turns old helper functions into thin wrappers around new code doesn't sound NFC; is that right? In a commit like that, the old helper functions' old implementations would go from being used to not being used.
Right, so such a commit would stop using the old implementation and start using the new implementation.
Whether that's NFC depends on whether there are differences in behavior between the old and the new implementations. If the new implementation is purely a new way of organizing the same ultimate logic as the old implementation has, so there's no change in behavior, then that commit is NFC.
I think this refactor is supposed to be at least mostly like that, right? At the end there's the commit that adds the fallback behavior -- but before that, IIUC it's not intended to change much behavior, if any. Is there some behavior it does intentionally change? Or, is there some behavior that it's hard to avoid having it change?
And another way to put that question is: if we do, before this commit, a change that looks something like:
export const getAvatarFromUser = (user: UserOrBot, realm: URL, size?: number): string =>
- getAvatarUrl(user.avatar_url, user.email, realm, size);
+ AvatarURL.fromUserOrBotData({ rawAvatarUrl: user.avatar_url, email: user.email, realm }).get(size).toString();
export const getAvatarFromMessage = (
message: Message | Outbox,
realm: URL,
size?: number,
-): string => getAvatarUrl(message.avatar_url, message.sender_email, realm, size);
+): string => {
+ const avatarUrl = AvatarURL.fromUserOrBotData({ rawAvatarUrl: message.avatar_url, email: message.sender_email, realm });
+ return avatarUrl.get(size).toString();
+};
then would that be NFC? And if not, why/how not?
If the answer is "yes", then arranging the series so that it contains that commit would be a nice way of demonstrating that. And if the answer is "no", then the detailed answer will be interesting for helping understand the branch... and rearranging things so that there is such a commit, if that's possible, would help make that detailed answer concrete, and help isolate the things that do change (in their own, focused commits) so that they're easier to see and to think about.
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.
Ah OK, got it, I think—I hadn't been thinking of calling .toString()
and returning a string, as you do in that example; that does make it easier to focus on how it might be NFC.
You've pointed to two exported functions, getAvatarFromUser
and getAvatarFromMessage
, being used to compute the actual URL from the pattern the server provides in avatar_url
. Awkwardly, there's a third: getAvatarUrl
. I have a hunch that it began its life as a helper function for those other two, but that UserAvatarWithPresence
saw it as an attractive option because it's sometimes passed info about a user, and sometimes about a message. I suspect we can make the same kind of change to getAvatarUrl
as you point out for those other two, though.
I find a few things that could be described as functional changes; maybe they're pretty minor (?):
In the old code, there's a tiny bug stemming from an assumption that, if avatar_url
is a string, it won't be a Gravatar URL. There are in fact two cases where we'll get a string that represents a Gravatar URL. These are noted in comments on AvatarURL.fromUserOrBotData
.
- If we don't announce
client_gravatar
. At that point in the branch, we don't announce it ingetMessages
. The server doesn't know that we can compute the Gravatar URL ourselves, so it just gives it to us. - If the server doesn't have EMAIL_ADDRESS_VISIBILITY_EVERYONE set. When that's set, real email addresses don't get sent to the client. But I think real email addresses are still supposed to determine Gravatar URLs, since I might have told Gravatar what my email address is, and what image to show for me. The server can't expect the client to compute the Gravatar URL for the real email address, since the server isn't sending real email addresses. So they just give the client the Gravatar URL for the real email address.
Reading past the first conditional in getAvatarUrl
, we appear to assume that the avatar_url
string will correspond to a relative or absolute URL for an uploaded avatar. This code is mostly harmless to a Gravatar URL string that has snuck through for one of the above reasons. It's an absolute URL, so realm
in new URL(avatarUrl, realm)
will be ignored. And if size
is greater than 100, it'll be run through a regex, but that's a no-op because Gravatar URLs don't have .png
in them.
I say it's "mostly harmless" because there is some behavior we probably don't intend: a Gravatar URL string sneaking through in these cases won't ever be set to the desired or Zulip-default size (80). The way to set a Gravatar URL to a certain size is to pass a query param s
, like s=100
. And that's just not done in these cases because the code isn't written to be aware that it might be working with a Gravatar URL.
The new code handles these cases by recognizing that we're dealing with Gravatar URLs, and they will be sized appropriately.
The old code uses a default size of 80. The new code doesn't use a default size, but treats a not-provided size as saying "I don't really mind what size I get." For Gravatar URLs, this means not passing the s
query param, and leaving it up to Gravatar to pick a size. For uploaded avatars, it means using the non--medium.png
version, which is what we also do when we pass a size that's 100 or less.
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've pointed to two exported functions,
getAvatarFromUser
andgetAvatarFromMessage
[...] I suspect we can make the same kind of change togetAvatarUrl
as you point out for those other two, though.
Ah, good catch, yes. I think that means a switchover commit could just edit getAvatarUrl
instead of those two callers of it.
- If the server doesn't have EMAIL_ADDRESS_VISIBILITY_EVERYONE set. When that's set, real email addresses don't get sent to the client. But I think real email addresses are still supposed to determine Gravatar URLs, since I might have told Gravatar what my email address is, and what image to show for me. The server can't expect the client to compute the Gravatar URL for the real email address, since the server isn't sending real email addresses. So they just give the client the Gravatar URL for the real email address.
Yep, this is correct -- when emails are hidden, the server still sends Gravatar URLs based on the real emails. (I'm not sure this is documented anywhere; I don't see it in the user-facing docs. But it's been confirmed by Tim in chat threads.)
- The old code uses a default size of 80. The new code doesn't use a default size, but treats a not-provided size as saying "I don't really mind what size I get." For Gravatar URLs, this means not passing the s query param, and leaving it up to Gravatar to pick a size. For uploaded avatars, it means using the non-
-medium.png
version, which is what we also do when we pass a size that's 100 or less.
Ah, that summary is helpful, thanks.
Looking up what Gravatar does in that case... its default is 80! I'd guess that's where our code's default of 80 came from, now that I see that. 🙂 So I think that means this doesn't actually represent a change in behavior, once that small fact from the Gravatar API (which is the API we're effectively using when constructing these Gravatar URLs) is taken into account.
- In the old code, there's a tiny bug stemming from an assumption that, if
avatar_url
is a string, it won't be a Gravatar URL. There are in fact two cases where we'll get a string that represents a Gravatar URL.
Mmm, yeah. And in master, or at the beginning of the branch, there's a third: the "double-processing" bug means we produce a Gravatar URL string prematurely, and then pass it through this avatar-URL logic again.
I say it's "mostly harmless" because there is some behavior we probably don't intend: a Gravatar URL string sneaking through in these cases won't ever be set to the desired [size]. The way to set a Gravatar URL to a certain size is to pass a query param
s
, likes=100
. And that's just not done in these cases because the code isn't written to be aware that it might be working with a Gravatar URL.
Ah indeed. I think I learned about some of this shortly after I wrote the comment above, in fact: while writing up and repro'ing #4305 (which grew from this comment elsewhere on the present PR: #4230 (comment) ). It's one reason the symptoms of that issue are different between Gravatars and uploaded avatars; though both cases are buggy, because when we do specify a size it's always too small (that's the core of #4305.)
I think this point gives us an example of this idea I described above, then:
And if the answer is "no", then the detailed answer will be interesting for helping understand the branch... and rearranging things so that there is such a commit, if that's possible, would help make that detailed answer concrete, and help isolate the things that do change (in their own, focused commits) so that they're easier to see and to think about.
Specifically: how about a commit early in the branch that does the equivalent of this:
if (tryParseUrl(rawAvatarUrl)?.origin === GravatarURL.ORIGIN) {
const hashMatch = /[0-9a-fA-F]{32}$/.exec(new URL(rawAvatarUrl).pathname);
if (hashMatch === null) {
const msg = 'Unexpected Gravatar URL shape from server.';
logging.error(msg, { value: rawAvatarUrl });
throw new Error(msg);
}
return GravatarURL.validateAndConstructInstance({ email, hash: hashMatch[0] });
}
but within the string-heavy, no-classes idiom of the existing code? I think that'd be a pretty small commit. The hard part here was in your sorting out the exact set of different situations that can occur. That commit would be a way of expressing this piece of what you learned; its commit message would probably reuse a chunk of the text from your comment here in this subthread.
Then with that change to fix the Gravatar-URL-string case, plus the "double-processing" fix... plus either a commit to adjust the old implementation's default-size behavior to match the new one's, or tweaking the new one's to be more like the old one's... I think at that point a switchover commit like the one I described would indeed be NFC. Does that sound right?
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.
Does that sound right?
Yes, it does! Thanks for these helpful pointers.
src/webview/html/messageAsHtml.js
Outdated
<img src="${avatarUrl | ||
.get() | ||
.toString()}" alt="${sender_full_name}" class="avatar-img" data-sender-id="${sender_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.
Could reduce the churn here by keeping the computation up at the const avatarUrl =
line above. I think that also keeps this template a bit easier to read.
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.
Sure, makes sense.
What do you think about the name of avatarUrl
in the const avatarUrl =
line you mentioned? Should we consider something to mark it as storing a specific usable URL with a size (like avatarUrlStr
)? I'm wondering the same thing about certain props that are addressed in your comment below (below?), #4230 (comment).
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.
What do you think about the name of
avatarUrl
in theconst avatarUrl =
line you mentioned? Should we consider something to mark it as storing a specific usable URL with a size (likeavatarUrlStr
)? I'm wondering the same thing about certain props that are addressed in your comment below (below?), #4230 (comment).
I think in the setup we've been in, where an "avatar URL" might be a sort of template for a URL or might be an actual concrete URL to go fetch, and in either case would be a string or some nullish value, that kind of distinction could help a fair bit to reduce the confusion about when we have a template and when we have a specific usable URL.
But after the changes in this PR, we have AvatarURL
for the templates, and we only use string
-- apart from at the edge, taking data straight from the server -- for actual concrete URLs. A type distinction is easier to consistently keep up than a naming distinction, and I think using avatarUrl
for both types of things is fine.
@@ -115,7 +114,7 @@ class Lightbox extends PureComponent<Props, State> { | |||
<LightboxHeader | |||
onPressBack={this.handlePressBack} | |||
timestamp={message.timestamp} | |||
avatarUrl={getAvatarFromMessage(message, auth.realm)} | |||
avatarUrl={message.avatar_url} |
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.
Perhaps another angle on the last couple of comments: this commit has the job of changing the shape of Message
, and that's enough to make it a significant commit.
When we're doing that, I think we should be able to leave unchanged which points in the code we take a user's avatar URL base and turn it into a specific usable URL with a size: we can call the new classes' get
methods at the same spots where we've been calling getAvatarFromMessage
and friends. So this is one of those spots.
Possibly then in a followup commit we'll want to change the types of things like this avatarUrl
prop, and move around the get
calls accordingly.
Thanks for the review! I've just pushed a new revision, but it doesn't yet address some concerns about preventing unintentional changes: #4230 (comment) Partly because I'll be at reduced capacity for a few days with my upcoming wrist surgery, so pushing some work seems better than pushing none, but also because I've posted a few questions on those comments that I'd appreciate your thoughts on. |
Greg points out [1] that it's time we start keeping track of a type for our initial data pre-transformation, since we're about to start transforming it. Here, start very small by saying what we expect `realm_users` and two of its friends to look like. We grab those expectations from the `avatar_url` field on `User` and on `CrossRealmBot`. [1] zulip#4230 (comment)
1d89afc
to
7b7b63a
Compare
Like it already says in the detail about `user_avatar_url_field_optional`, but on the field itself [1]. [1] zulip/zulip-mobile#4230 (comment)
Greg points out [1] that it's time we start keeping track of a type for our initial data pre-transformation, since we're about to start transforming it. Here, start very small by saying what we expect `realm_users` and two of its friends to look like. We grab those expectations from the `avatar_url` field on `User` and on `CrossRealmBot`. [1] zulip#4230 (comment)
7b7b63a
to
690a4dc
Compare
I've just pushed a revision that should hopefully make things clearer, following #4230 (comment) (but also please see #4230 (comment)). |
Greg points out [1] that it's time we start keeping track of a type for our initial data pre-transformation, since we're about to start transforming it. Here, start very small by saying what we expect `realm_users` and two of its friends to look like. We grab those expectations from the `avatar_url` field on `User` and on `CrossRealmBot`. [1] #4230 (comment)
690a4dc
to
b1c8d5a
Compare
We'll use this soon in a test in the `failure` block.
We weren't ever actually passing a void `avatarUrl`, and an empty string is nonsense for an avatar URL. Also make a note about another default prop that would be good to remove [1], but that is still operating in one case. [1] zulip#4230 (comment)
At this point, we switch out the old string-heavy, no-classes implementation of "compute the actual URL from the pattern the server provides in `avatar_url`" for the new one we've just introduced. We expect this to be NFC; we've combed through the possible things we could get from the server and ironed out various wrinkles [1]. [1] zulip#4230 (comment)
And have the UI logic that handles avatars for messages accept the new form. We'll do the same for UserOrBot objects in the next commit. There's a small amount of churn that comes from not converting users and messages in the same commit; I've added temporary code comments where this happens. For the `GET /messages` response, the natural place to do the conversion is in `migrateMessages`. For events, it's in `eventToAction`.
For the `/register` response, we added a `transform` function with a `migrateUserOrBot` helper in a recent commit, to be used here. For events (for added and updated users), the natural place to do this is in `eventToAction`. We don't reuse `migrateUserOrBot` from `registerForEvents` because users and bots are likely to have different shapes in the `/register` response and in events, and we'll want to handle those shapes separately.
…ply. Now that the three unioned types are all `AvatarURL`. Also, make a slight change to the contract for this prop: although it probably will remain so in practice, the value doesn't have to come from `.avatar_url` on a `Message` or `UserOrBot`; any `AvatarURL` will be handled appropriately.
Like we did in the previous commit, for `UserAvatarWithPresence`.
It was confusing to rely on this instead of putting avatar URLs into a standard form at the edge. Now, it has no call sites, so they can disappear! :)
I'd thought we were already doing this, but apparently not. See doc at https://zulipchat.com/api/get-messages#parameters.
See the point on `user_avatar_url_field_optional` at https://zulipchat.com/api/register-queue. This will reduce the volume of data that will have to be sent in the `/register` response, which should speed up the initial load. Now, the server may choose, at its own discretion, to omit the `avatar_url` field for users in the `/register` response [1], and we'll handle it by using the `/avatar/{user_id}` endpoint. [1] As of the introduction of the client capability (Zulip 3.0, feature level 18), it makes this decision based on whether the user has been inactive for a long time (using the `long_term_idle` field), but this is an implementation detail and should be expected to change without notice. Fixes: zulip#4157
b2abbf3
to
c642677
Compare
OK, done! Thanks again for the reviews! 🙂 |
As Greg points out, the effect of this default prop has been to suppress the presence-status indicator for the sender in the lightbox header. And since that omission is quite unlikely to be intentional, we should fix it by passing down the sender's email. So, do. [1] zulip#4230 (comment)
As Greg points out, the effect of this default prop has been to suppress the presence-status indicator for the sender in the lightbox header. And since that omission is quite unlikely to be intentional, we should fix it by passing down the sender's email. [1] So, do. [1] zulip#4230 (comment)
…tor. Specifically, in its static `validateAndConstructInstance` method. In working on the recently merged zulip#4230, we found that calling `new URL` with ordinary input is quite expensive, and so we arranged to not call it when rehydrating. We left the call in `validateAndConstructInstance`, though, thinking we couldn't possibly remove it and still validate the raw server data properly at the edge. When we did some performance benchmarking after zulip#4230 was merged [1], though, we found that `api.registerForEvents` started to take a lot longer after zulip#4230 than before it. Profiling [2] suggested that we could help speed things up if we could just stop calling `new URL` in `validateAndConstructInstance`. Looking closer, thankfully, it's possible and reasonably easy. We would normally avoid string concatenation for URLs entirely, but this is a case where we can do it in a fairly principled way (see the implementation comment in the diff). [1] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1080919 [2] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081240 [3] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081253
…tor. Specifically, in its static `validateAndConstructInstance` method. In working on the recently merged zulip#4230, we found that calling `new URL` with ordinary input is quite expensive, and so we arranged to not call it when rehydrating. We left the call in `validateAndConstructInstance`, though, thinking we couldn't possibly remove it and still validate the raw server data properly at the edge. When we did some performance benchmarking after zulip#4230 was merged [1], though, we found that `api.registerForEvents` started to take a lot longer after zulip#4230 than before it. Profiling [2] suggested that we could help speed things up if we could just stop calling `new URL` in `validateAndConstructInstance`. Looking closer, thankfully, it's possible and reasonably easy. We would normally avoid string concatenation for URLs entirely, but this is a case where we can do it in a fairly principled way (see the implementation comment in the diff). [1] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1080919 [2] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081240 [3] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081253
As Greg points out, the effect of this default prop has been to suppress the presence-status indicator for the sender in the lightbox header. And since that omission is quite unlikely to be intentional, we should fix it by passing down the sender's email. [1] So, do. [1] zulip#4230 (comment)
…tor. Specifically, in its static `validateAndConstructInstance` method. In working on the recently merged zulip#4230, we found that calling `new URL` with ordinary input is quite expensive, and so we arranged to not call it when rehydrating. We left the call in `validateAndConstructInstance`, though, thinking we couldn't possibly remove it and still validate the raw server data properly at the edge. When we did some performance benchmarking after zulip#4230 was merged [1], though, we found that `api.registerForEvents` started to take a lot longer after zulip#4230 than before it. Profiling [2] suggested that we could help speed things up if we could just stop calling `new URL` in `validateAndConstructInstance`. Looking closer, thankfully, it's possible and reasonably easy. We would normally avoid string concatenation for URLs entirely, but this is a case where we can do it in a fairly principled way (see the implementation comment in the diff). [1] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1080919 [2] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081240 [3] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081253
…tor. Specifically, in its static `validateAndConstructInstance` method. In working on the recently merged zulip#4230, we found that calling `new URL` with ordinary input is quite expensive, and so we arranged to not call it when rehydrating. We left the call in `validateAndConstructInstance`, though, thinking we couldn't possibly remove it and still validate the raw server data properly at the edge. When we did some performance benchmarking after zulip#4230 was merged [1], though, we found that `api.registerForEvents` started to take a lot longer after zulip#4230 than before it. Profiling [2] suggested that we could help speed things up if we could just stop calling `new URL` in `validateAndConstructInstance`. Looking closer, thankfully, it's possible and reasonably easy. We would normally avoid string concatenation for URLs entirely, but this is a case where we can do it in a fairly principled way (see the implementation comment in the diff). [1] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1080919 [2] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081240 [3] https://chat.zulip.org/#narrow/stream/243-mobile-team/topic/user_avatar_url_field_optional/near/1081253
This follows up on zulip#4344 to address part of the remaining performance regression from zulip#4230. We still construct URL objects in the `get` methods, lazily and memoized; but never up front when simply taking in all the initial data from the server, or the rehydrated data we've stored. This should save a further slice of time in the initial load.
This follows up on #4344 to address part of the remaining performance regression from #4230. We still construct URL objects in the `get` methods, lazily and memoized; but never up front when simply taking in all the initial data from the server, or the rehydrated data we've stored. This should save a further slice of time in the initial load.
Following #4171 and #4216, here's a new approach to grabbing the correct URL for a user's avatar in a bunch of places, at the right size, in a crunchy-shell way. It also makes it much easier to fix #4157 and fixes it.
Discussion on CZO around here.
Some questions for review:
AvatarURL.fromMessageData
andAvatarURL.fromUserOrBotData
, I realized that they're quite similar. I didn't jump in and deduplicate them yet, though, in case that seeming similarity was deceptive. In particular, therawAvatarUrl
param is meant to be raw data straight from the server, and that has a slightly different form between users and messages. More importantly, maybe, it's reasonable to expect that those differences will remain and maybe grow. But maybe not dramatically, and maybe one will always be a subtype of the other.state.users
, but not messages instate.messages
. So if someone changes their avatar URL, you won't see the new one right away in the message list. There's some work to do before we get there; see discussion on implementation.avatar_url
, I'm havingaction.person
on EVENT_USER_UPDATE only contain the fields we want to be overwritten in the user object. I've started out small, and I'm only ever having it overwrite theavatar_url
property.eventToAction
, I think) aware of the different well-defined chunks of properties we might get at a time. The nice new documentation for the event shows that a handful of properties will be present if the avatar URL is changed, a different handful will be present if the timezone changes, etc. In this branch, I haven't acknowledged that detail, thinking it's simpler to just go property by property: if it's there, we'll consider updating it; if not, we won't.Fixes: #4157
EDIT:
Fixes: #4305