UX: Stop avatar flicker when opening user menu on Safari (#26511)

When opening the user menu, we display old cached data, and then replace it with fresh data immediately afterwards. The vast majority of the time the data is unchanged, and so there is no visible change. When rendering HTML elements directly, Ember realizes that there is no change, and does not make any changes to the DOM. Great!

However, our `avatar` helper returns a blob of HTML. With raw HTML, Ember does not make any attempt to 'diff' the existing DOM. Instead, it replaces the old string with the new string. That can be a little wasteful, but normally it's not a big deal. But, when it comes to `<img lazy="lazy"`, re-rendering the `img` element causes a visible flicker in Safari.

To work around that, this commit replaces the `{{avatar}}` helper with an ember-rendered `<img` element. Now that Ember is responsible for rendering, it can detect there is no real change to the attributes and skip it, thereby avoiding the flicker.

If we find ourselves doing this more frequently, we may want to consider creating an `<Avatar` component. But for now, I think it's simple enough to justify building the `<img` manually in this case.
This commit is contained in:
David Taylor 2024-04-04 11:46:38 +01:00 committed by GitHub
parent eda4b12260
commit 37ae1708a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,10 +1,24 @@
import avatar from "discourse/helpers/bound-avatar-template";
import concatClass from "discourse/helpers/concat-class";
import icon from "discourse-common/helpers/d-icon";
import { avatarUrl, translateSize } from "discourse-common/lib/avatar-utils";
const avatarPx = translateSize("small");
const IconAvatar = <template>
<div class={{concatClass "icon-avatar" @data.classNames}}>
{{avatar @data.avatarTemplate "small"}}
{{!--
avoiding {{avatar}} helper because its html would be fully
re-rendered whenever arguments change, even if the argument values
are identical. On some browsers, re-rendering a lazy-loaded image
causes a visible flicker.
--}}
<img
lazy="lazy"
src={{avatarUrl @data.avatarTemplate "small"}}
width={{avatarPx}}
height={{avatarPx}}
class="avatar"
/>
<div class="icon-avatar__icon-wrapper">
{{icon @data.icon}}
</div>