Render custom images in reactions (#11087)

* Add support for rendering custom emojis in reactions

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Include custom reaction short names in tooltips

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Use custom reaction shortcode for accessibility

This uses the shortcode in the following places:

* The aria-label of the reaction buttons
* The alt text for the reaction image

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Remove explicit instantiation of `customReactionName` variable and add types

Co-authored-by: Šimon Brandner <simon.bra.ag@gmail.com>

* Put custom reaction images behind a labs flag

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Use UnstableValue for finding the shortcode

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
Signed-off-by: Sumner Evans <sumner@beeper.com>

* Move calculation of whether to render custom reaction images up to ReactionRow

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Make alt text more friendly when custom reaction doesn't have shortcode

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Add test for ReactionsRowButton

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Apply suggestions from code review

Co-authored-by: Šimon Brandner <simon.bra.ag@gmail.com>

* Don't use Optional

Signed-off-by: Sumner Evans <sumner@beeper.com>

* Fix ReactionsRowButton test

Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
Signed-off-by: Sumner Evans <sumner@beeper.com>

---------

Signed-off-by: Sumner Evans <sumner@beeper.com>
Co-authored-by: Tulir Asokan <tulir@maunium.net>
Co-authored-by: Šimon Brandner <simon.bra.ag@gmail.com>
Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Sumner Evans 2023-09-01 04:16:24 -06:00 committed by GitHub
parent d551469543
commit a54f2ff878
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 283 additions and 6 deletions

View file

@ -18,13 +18,15 @@ import React from "react";
import classNames from "classnames";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { mediaFromMxc } from "../../../customisations/Media";
import { _t } from "../../../languageHandler";
import { formatCommaSeparatedList } from "../../../utils/FormattingUtils";
import dis from "../../../dispatcher/dispatcher";
import ReactionsRowButtonTooltip from "./ReactionsRowButtonTooltip";
import AccessibleButton from "../elements/AccessibleButton";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
interface IProps {
import { REACTION_SHORTCODE_KEY } from "./ReactionsRow";
export interface IProps {
// The event we're displaying reactions for
mxEvent: MatrixEvent;
// The reaction content / key / emoji
@ -37,6 +39,8 @@ interface IProps {
myReactionEvent?: MatrixEvent;
// Whether to prevent quick-reactions by clicking on this reaction
disabled?: boolean;
// Whether to render custom image reactions
customReactionImagesEnabled?: boolean;
}
interface IState {
@ -100,27 +104,56 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
content={content}
reactionEvents={reactionEvents}
visible={this.state.tooltipVisible}
customReactionImagesEnabled={this.props.customReactionImagesEnabled}
/>
);
}
const room = this.context.getRoom(mxEvent.getRoomId());
let label: string | undefined;
let customReactionName: string | undefined;
if (room) {
const senders: string[] = [];
for (const reactionEvent of reactionEvents) {
const member = room.getMember(reactionEvent.getSender()!);
senders.push(member?.name || reactionEvent.getSender()!);
customReactionName =
(this.props.customReactionImagesEnabled &&
REACTION_SHORTCODE_KEY.findIn(reactionEvent.getContent())) ||
undefined;
}
const reactors = formatCommaSeparatedList(senders, 6);
if (content) {
label = _t("%(reactors)s reacted with %(content)s", { reactors, content });
label = _t("%(reactors)s reacted with %(content)s", {
reactors,
content: customReactionName || content,
});
} else {
label = reactors;
}
}
let reactionContent = (
<span className="mx_ReactionsRowButton_content" aria-hidden="true">
{content}
</span>
);
if (this.props.customReactionImagesEnabled && content.startsWith("mxc://")) {
const imageSrc = mediaFromMxc(content).srcHttp;
if (imageSrc) {
reactionContent = (
<img
className="mx_ReactionsRowButton_content"
alt={customReactionName || _t("Custom reaction")}
src={imageSrc}
width="16"
height="16"
/>
);
}
}
return (
<AccessibleButton
className={classes}
@ -130,9 +163,7 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
onMouseOver={this.onMouseOver}
onMouseLeave={this.onMouseLeave}
>
<span className="mx_ReactionsRowButton_content" aria-hidden="true">
{content}
</span>
{reactionContent}
<span className="mx_ReactionsRowButton_count" aria-hidden="true">
{count}
</span>