From 6aa86a858f68a29bbda9438dc4a41a3915345d3c Mon Sep 17 00:00:00 2001 From: Germain Date: Wed, 30 Aug 2023 12:47:35 +0100 Subject: [PATCH] Fixes read receipt avatar offset (#11483) * Fixes read receipt avatar offset Fixes https://github.com/vector-im/element-web/issues/26059 * Fix avatar collapsing in thread list Fixes https://github.com/vector-im/element-web/issues/26064 * Make composer pills use new avatar design Fixes https://github.com/vector-im/element-web/issues/26067 * Update snapshots * Update UserInfo snapshot * Update HTMLExport snapshot * Fixes avatar placeholder font Fixes https://github.com/vector-im/element-web/issues/26061 --- res/css/views/avatars/_BaseAvatar.pcss | 7 +++ .../views/rooms/_BasicMessageComposer.pcss | 2 +- res/css/views/rooms/_ReadReceiptGroup.pcss | 8 ++-- res/css/views/rooms/_ThreadSummary.pcss | 1 + .../wysiwyg_composer/components/_Editor.pcss | 4 +- src/Avatar.ts | 31 ++++++++++--- src/components/views/avatars/BaseAvatar.tsx | 2 +- .../wysiwyg_composer/utils/autocomplete.ts | 46 +++++++++++++++++-- .../__snapshots__/RoomView-test.tsx.snap | 7 +++ .../SpaceHierarchy-test.tsx.snap | 4 ++ .../__snapshots__/UserMenu-test.tsx.snap | 1 + .../__snapshots__/RoomAvatar-test.tsx.snap | 3 ++ .../__snapshots__/DialogSidebar-test.tsx.snap | 1 + ...nageRestrictedJoinRuleDialog-test.tsx.snap | 1 + .../__snapshots__/AppTile-test.tsx.snap | 4 ++ .../__snapshots__/FacePile-test.tsx.snap | 1 + .../elements/__snapshots__/Pill-test.tsx.snap | 8 ++++ .../views/messages/TextualBody-test.tsx | 2 +- .../__snapshots__/TextualBody-test.tsx.snap | 6 +++ .../RoomSummaryCard-test.tsx.snap | 1 + .../__snapshots__/UserInfo-test.tsx.snap | 1 + .../PinnedEventTile-test.tsx.snap | 1 + .../RoomPreviewBar-test.tsx.snap | 5 ++ .../__snapshots__/RoomTile-test.tsx.snap | 4 ++ .../AddExistingToSpaceDialog-test.tsx.snap | 1 + 25 files changed, 132 insertions(+), 20 deletions(-) diff --git a/res/css/views/avatars/_BaseAvatar.pcss b/res/css/views/avatars/_BaseAvatar.pcss index 52fd8452d3e..dd643ebfb58 100644 --- a/res/css/views/avatars/_BaseAvatar.pcss +++ b/res/css/views/avatars/_BaseAvatar.pcss @@ -22,3 +22,10 @@ limitations under the License. color: white !important; } } + +button.mx_BaseAvatar { + /* The user agent stylesheet overrides the font-size in this scenario + And that breaks the alignment, emojis, and all sorts of things + */ + font-size: inherit; +} diff --git a/res/css/views/rooms/_BasicMessageComposer.pcss b/res/css/views/rooms/_BasicMessageComposer.pcss index 7b88a058153..e09eaa5a04f 100644 --- a/res/css/views/rooms/_BasicMessageComposer.pcss +++ b/res/css/views/rooms/_BasicMessageComposer.pcss @@ -79,7 +79,7 @@ limitations under the License. height: $font-16px; margin-inline-end: 0.24rem; background: var(--avatar-background), $background; - color: $avatar-initial-color; + color: var(--avatar-color, $avatar-initial-color); background-repeat: no-repeat; background-size: $font-16px; border-radius: $font-16px; diff --git a/res/css/views/rooms/_ReadReceiptGroup.pcss b/res/css/views/rooms/_ReadReceiptGroup.pcss index 16efa8a5130..b09b818a7a0 100644 --- a/res/css/views/rooms/_ReadReceiptGroup.pcss +++ b/res/css/views/rooms/_ReadReceiptGroup.pcss @@ -49,13 +49,11 @@ limitations under the License. height: 100%; .mx_BaseAvatar { + box-sizing: content-box; position: absolute; - display: inline-block; - height: 14px; - width: 14px; border: 1px solid $background; - border-radius: 100%; - + width: 14px; + height: 14px; will-change: left, top; transition: left var(--transition-short) ease-out, top var(--transition-standard) ease-out; } diff --git a/res/css/views/rooms/_ThreadSummary.pcss b/res/css/views/rooms/_ThreadSummary.pcss index 34fe50e9898..78a9d9e5292 100644 --- a/res/css/views/rooms/_ThreadSummary.pcss +++ b/res/css/views/rooms/_ThreadSummary.pcss @@ -123,6 +123,7 @@ limitations under the License. .mx_ThreadSummary_avatar { margin-inline-end: $spacing-8; + flex-shrink: 0; } .mx_ThreadSummary_icon { diff --git a/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss b/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss index 50f76635983..c4591c4c073 100644 --- a/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss +++ b/res/css/views/rooms/wysiwyg_composer/components/_Editor.pcss @@ -148,8 +148,8 @@ limitations under the License. background-size: $font-16px; border-radius: $font-16px; - color: $avatar-initial-color; - font-weight: normal; + color: var(--avatar-color, $avatar-initial-color); + font-weight: bold; font-size: $font-10-4px; } } diff --git a/src/Avatar.ts b/src/Avatar.ts index 13dd2800669..7e54c634c59 100644 --- a/src/Avatar.ts +++ b/src/Avatar.ts @@ -15,12 +15,21 @@ limitations under the License. */ import { RoomMember, User, Room, ResizeMethod } from "matrix-js-sdk/src/matrix"; +import { useIdColorHash } from "@vector-im/compound-web"; import DMRoomMap from "./utils/DMRoomMap"; import { mediaFromMxc } from "./customisations/Media"; import { isLocalRoom } from "./utils/localRoom/isLocalRoom"; import { getFirstGrapheme } from "./utils/strings"; +/** + * Hardcoded from the Compound colors. + * Shade for background as defined in the compound web implementation + * https://github.com/vector-im/compound-web/blob/main/src/components/Avatar + */ +const AVATAR_BG_COLORS = ["#e9f2ff", "#faeefb", "#e3f7ed", "#ffecf0", "#ffefe4", "#e3f5f8", "#f1efff", "#e0f8d9"]; +const AVATAR_TEXT_COLORS = ["#043894", "#671481", "#004933", "#7e0642", "#850000", "#004077", "#4c05b5", "#004b00"]; + // Not to be used for BaseAvatar urls as that has similar default avatar fallback already export function avatarUrlForMember( member: RoomMember | undefined, @@ -41,6 +50,18 @@ export function avatarUrlForMember( return url; } +/** + * Determines the HEX color to use in the avatar pills + * @param id the user or room ID + * @returns the text color to use on the avatar + */ +export function getAvatarTextColor(id: string): string { + // eslint-disable-next-line react-hooks/rules-of-hooks + const index = useIdColorHash(id); + + return AVATAR_TEXT_COLORS[index - 1]; +} + export function avatarUrlForUser( user: Pick, width: number, @@ -85,16 +106,12 @@ const colorToDataURLCache = new Map(); export function defaultAvatarUrlForString(s: string): string { if (!s) return ""; // XXX: should never happen but empirically does by evidence of a rageshake - const defaultColors = ["#0DBD8B", "#368bd6", "#ac3ba8"]; - let total = 0; - for (let i = 0; i < s.length; ++i) { - total += s.charCodeAt(i); - } - const colorIndex = total % defaultColors.length; + // eslint-disable-next-line react-hooks/rules-of-hooks + const colorIndex = useIdColorHash(s); // overwritten color value in custom themes const cssVariable = `--avatar-background-colors_${colorIndex}`; const cssValue = getComputedStyle(document.body).getPropertyValue(cssVariable); - const color = cssValue || defaultColors[colorIndex]; + const color = cssValue || AVATAR_BG_COLORS[colorIndex - 1]; let dataUrl = colorToDataURLCache.get(color); if (!dataUrl) { // validate color as this can come from account_data diff --git a/src/components/views/avatars/BaseAvatar.tsx b/src/components/views/avatars/BaseAvatar.tsx index d01a5affe05..40932cf7248 100644 --- a/src/components/views/avatars/BaseAvatar.tsx +++ b/src/components/views/avatars/BaseAvatar.tsx @@ -99,7 +99,7 @@ const BaseAvatar: React.FC = (props) => { const { name, idName, - title, + title = "", url, urls, size = "40px", diff --git a/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts b/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts index c89e9d706bb..c7a8670c6b2 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/autocomplete.ts @@ -84,6 +84,25 @@ export function getMentionDisplayText(completion: ICompletion, client: MatrixCli return ""; } +function getCSSProperties({ + url, + initialLetter, + id = "", +}: { + url: string; + initialLetter?: string; + id: string; +}): string { + const cssProperties = [`--avatar-background: url(${url})`, `--avatar-letter: '${initialLetter}'`]; + + const textColor = Avatar.getAvatarTextColor(id); + if (textColor) { + cssProperties.push(textColor); + } + + return cssProperties.join("; "); +} + /** * For a given completion, the attributes will change depending on the completion type * @@ -118,7 +137,14 @@ export function getMentionAttributes( } attributes.set("data-mention-type", completion.type); - attributes.set("style", `--avatar-background: url(${avatarUrl}); --avatar-letter: '${initialLetter}'`); + attributes.set( + "style", + getCSSProperties({ + url: avatarUrl, + initialLetter, + id: mentionedMember.userId, + }), + ); } else if (completion.type === "room") { // logic as used in RoomPillPart.setAvatar in parts.ts const mentionedRoom = getRoomFromCompletion(completion, client); @@ -132,7 +158,14 @@ export function getMentionAttributes( } attributes.set("data-mention-type", completion.type); - attributes.set("style", `--avatar-background: url(${avatarUrl}); --avatar-letter: '${initialLetter}'`); + attributes.set( + "style", + getCSSProperties({ + url: avatarUrl, + initialLetter, + id: mentionedRoom?.roomId ?? aliasFromCompletion, + }), + ); } else if (completion.type === "at-room") { // logic as used in RoomPillPart.setAvatar in parts.ts, but now we know the current room // from the arguments passed @@ -145,7 +178,14 @@ export function getMentionAttributes( } attributes.set("data-mention-type", completion.type); - attributes.set("style", `--avatar-background: url(${avatarUrl}); --avatar-letter: '${initialLetter}'`); + attributes.set( + "style", + getCSSProperties({ + url: avatarUrl, + initialLetter, + id: room.roomId, + }), + ); } return attributes; diff --git a/test/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/components/structures/__snapshots__/RoomView-test.tsx.snap index 19df74f76d6..6d678141dcf 100644 --- a/test/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/test/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -24,6 +24,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1 data-type="round" role="presentation" style="--cpd-avatar-size: 24px;" + title="" > u @@ -106,6 +107,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`] data-type="round" role="presentation" style="--cpd-avatar-size: 24px;" + title="" > u @@ -185,6 +187,7 @@ exports[`RoomView for a local room in state ERROR should match the snapshot 1`] data-type="round" role="button" style="--cpd-avatar-size: 52px;" + title="" > u @@ -273,6 +276,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] = data-type="round" role="presentation" style="--cpd-avatar-size: 24px;" + title="" > u @@ -352,6 +356,7 @@ exports[`RoomView for a local room in state NEW should match the snapshot 1`] = data-type="round" role="button" style="--cpd-avatar-size: 52px;" + title="" > u @@ -515,6 +520,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t data-type="round" role="presentation" style="--cpd-avatar-size: 24px;" + title="" > u @@ -593,6 +599,7 @@ exports[`RoomView for a local room in state NEW that is encrypted should match t data-type="round" role="button" style="--cpd-avatar-size: 52px;" + title="" > u diff --git a/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap b/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap index c99d5ee07d6..1e7edb176cd 100644 --- a/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap +++ b/test/components/structures/__snapshots__/SpaceHierarchy-test.tsx.snap @@ -77,6 +77,7 @@ exports[`SpaceHierarchy renders 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 20px;" + title="" > U @@ -147,6 +148,7 @@ exports[`SpaceHierarchy renders 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 20px;" + title="" > U @@ -218,6 +220,7 @@ exports[`SpaceHierarchy renders 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 20px;" + title="" > N @@ -295,6 +298,7 @@ exports[`SpaceHierarchy renders 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 20px;" + title="" > N diff --git a/test/components/structures/__snapshots__/UserMenu-test.tsx.snap b/test/components/structures/__snapshots__/UserMenu-test.tsx.snap index 54512dd379a..d8003602f49 100644 --- a/test/components/structures/__snapshots__/UserMenu-test.tsx.snap +++ b/test/components/structures/__snapshots__/UserMenu-test.tsx.snap @@ -24,6 +24,7 @@ exports[` when rendered should render as expected 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 32px;" + title="" > u diff --git a/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap b/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap index 20498fd510d..cd545107be7 100644 --- a/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap +++ b/test/components/views/avatars/__snapshots__/RoomAvatar-test.tsx.snap @@ -9,6 +9,7 @@ exports[`RoomAvatar should render as expected for a DM room 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > D @@ -24,6 +25,7 @@ exports[`RoomAvatar should render as expected for a LocalRoom 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > l @@ -39,6 +41,7 @@ exports[`RoomAvatar should render as expected for a Room 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > t diff --git a/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap b/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap index 3a4810e2e2e..11178c8cbd1 100644 --- a/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap +++ b/test/components/views/beacon/__snapshots__/DialogSidebar-test.tsx.snap @@ -38,6 +38,7 @@ exports[` renders sidebar correctly with beacons 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 32px;" + title="" > diff --git a/test/components/views/dialogs/__snapshots__/ManageRestrictedJoinRuleDialog-test.tsx.snap b/test/components/views/dialogs/__snapshots__/ManageRestrictedJoinRuleDialog-test.tsx.snap index b26369b3083..0e29403ed56 100644 --- a/test/components/views/dialogs/__snapshots__/ManageRestrictedJoinRuleDialog-test.tsx.snap +++ b/test/components/views/dialogs/__snapshots__/ManageRestrictedJoinRuleDialog-test.tsx.snap @@ -77,6 +77,7 @@ exports[` should list spaces which are not par data-type="round" role="presentation" style="--cpd-avatar-size: 20px;" + title="" > O diff --git a/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap b/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap index 0693f40f660..682bb61314a 100644 --- a/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/AppTile-test.tsx.snap @@ -109,6 +109,7 @@ exports[`AppTile for a pinned widget should render 1`] = ` data-testid="avatar-img" data-type="round" style="--cpd-avatar-size: 20px;" + title="" > u @@ -352,6 +355,7 @@ exports[`AppTile preserves non-persisted widget on container move 1`] = ` data-testid="avatar-img" data-type="round" style="--cpd-avatar-size: 20px;" + title="" > renders with a tooltip 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > 4 diff --git a/test/components/views/elements/__snapshots__/Pill-test.tsx.snap b/test/components/views/elements/__snapshots__/Pill-test.tsx.snap index 1015798287d..a7bedd5f482 100644 --- a/test/components/views/elements/__snapshots__/Pill-test.tsx.snap +++ b/test/components/views/elements/__snapshots__/Pill-test.tsx.snap @@ -41,6 +41,7 @@ exports[` should render the expected pill for @room 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > R @@ -72,6 +73,7 @@ exports[` should render the expected pill for a known user not in the room data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > U @@ -103,6 +105,7 @@ exports[` should render the expected pill for a message in another room 1` data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > R @@ -134,6 +137,7 @@ exports[` should render the expected pill for a message in the same room 1 data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > U @@ -165,6 +169,7 @@ exports[` should render the expected pill for a room alias 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > R @@ -196,6 +201,7 @@ exports[` should render the expected pill for a space 1`] = ` data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > S @@ -250,6 +256,7 @@ exports[` when rendering a pill for a room should render the expected pill data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > R @@ -281,6 +288,7 @@ exports[` when rendering a pill for a user in the room should render as ex data-type="round" role="presentation" style="--cpd-avatar-size: 16px;" + title="" > U diff --git a/test/components/views/messages/TextualBody-test.tsx b/test/components/views/messages/TextualBody-test.tsx index 5a51e6a2b71..a6b22a74123 100644 --- a/test/components/views/messages/TextualBody-test.tsx +++ b/test/components/views/messages/TextualBody-test.tsx @@ -199,7 +199,7 @@ describe("", () => { const { container } = getComponent({ mxEvent: ev }); const content = container.querySelector(".mx_EventTile_body"); expect(content.innerHTML).toMatchInlineSnapshot( - `"Chat with Member"`, + `"Chat with Member"`, ); }); diff --git a/test/components/views/messages/__snapshots__/TextualBody-test.tsx.snap b/test/components/views/messages/__snapshots__/TextualBody-test.tsx.snap index b9c507231ce..6c3434ce728 100644 --- a/test/components/views/messages/__snapshots__/TextualBody-test.tsx.snap +++ b/test/components/views/messages/__snapshots__/TextualBody-test.tsx.snap @@ -62,6 +62,7 @@ exports[` renders formatted m.text correctly pills appear for an data-testid="avatar-img" data-type="round" style="--cpd-avatar-size: 16px;" + title="" > renders formatted m.text correctly pills appear for eve data-testid="avatar-img" data-type="round" style="--cpd-avatar-size: 16px;" + title="" > renders formatted m.text correctly pills appear for roo data-testid="avatar-img" data-type="round" style="--cpd-avatar-size: 16px;" + title="" > renders formatted m.text correctly pills get injected c data-testid="avatar-img" data-type="round" style="--cpd-avatar-size: 16px;" + title="" > renders plain-text m.text correctly should pillify a pe href="https://matrix.to/#/!room1:example.com/%event_id%" aria-describedby="mx_Pill_0.123456" > diff --git a/test/components/views/rooms/__snapshots__/RoomPreviewBar-test.tsx.snap b/test/components/views/rooms/__snapshots__/RoomPreviewBar-test.tsx.snap index d193fbea780..888a42edaee 100644 --- a/test/components/views/rooms/__snapshots__/RoomPreviewBar-test.tsx.snap +++ b/test/components/views/rooms/__snapshots__/RoomPreviewBar-test.tsx.snap @@ -29,6 +29,7 @@ exports[` message case AskToJoin renders the corresponding mes data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > R @@ -54,6 +55,7 @@ exports[` message case AskToJoin renders the corresponding mes data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > ? @@ -220,6 +222,7 @@ exports[` with an invite with an invited email when client has data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > R @@ -278,6 +281,7 @@ exports[` with an invite without an invited email for a dm roo data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > R @@ -343,6 +347,7 @@ exports[` with an invite without an invited email for a non-dm data-type="round" role="presentation" style="--cpd-avatar-size: 36px;" + title="" > R diff --git a/test/components/views/rooms/__snapshots__/RoomTile-test.tsx.snap b/test/components/views/rooms/__snapshots__/RoomTile-test.tsx.snap index 0ece644a769..397d1cd2210 100644 --- a/test/components/views/rooms/__snapshots__/RoomTile-test.tsx.snap +++ b/test/components/views/rooms/__snapshots__/RoomTile-test.tsx.snap @@ -20,6 +20,7 @@ exports[`RoomTile when message previews are enabled and there is a message in a data-type="round" role="presentation" style="--cpd-avatar-size: 32px;" + title="" > ! @@ -94,6 +95,7 @@ exports[`RoomTile when message previews are enabled and there is a message in th data-type="round" role="presentation" style="--cpd-avatar-size: 32px;" + title="" > ! @@ -168,6 +170,7 @@ exports[`RoomTile when message previews are enabled should render a room without data-type="round" role="presentation" style="--cpd-avatar-size: 32px;" + title="" > ! @@ -230,6 +233,7 @@ exports[`RoomTile when message previews are not enabled should render the room 1 data-type="round" role="presentation" style="--cpd-avatar-size: 32px;" + title="" > ! diff --git a/test/components/views/spaces/__snapshots__/AddExistingToSpaceDialog-test.tsx.snap b/test/components/views/spaces/__snapshots__/AddExistingToSpaceDialog-test.tsx.snap index 13603285954..62791aea985 100644 --- a/test/components/views/spaces/__snapshots__/AddExistingToSpaceDialog-test.tsx.snap +++ b/test/components/views/spaces/__snapshots__/AddExistingToSpaceDialog-test.tsx.snap @@ -31,6 +31,7 @@ exports[` looks as expected 1`] = ` data-testid="avatar-img" data-type="square" style="--cpd-avatar-size: 40px;" + title="" >