Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #12549 from matrix-org/florianduros/tooltip/legacy…
Browse files Browse the repository at this point in the history
…-tooltip

Tooltip: Use tooltip compound instead of react-sdk tooltip
  • Loading branch information
dbkr authored May 23, 2024
2 parents 1190de9 + d9f718e commit 88e8e2d
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 279 deletions.
3 changes: 3 additions & 0 deletions src/components/views/elements/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ export interface ITooltipProps {

type State = Partial<Pick<CSSProperties, "display" | "right" | "top" | "transform" | "left">>;

/**
* @deprecated Use [compound tooltip](https://element-hq.github.io/compound-web/?path=/docs/tooltip--docs) instead
*/
export default class Tooltip extends React.PureComponent<ITooltipProps, State> {
private static container: HTMLElement;
private parent: Element | null = null;
Expand Down
70 changes: 18 additions & 52 deletions src/components/views/messages/ReactionsRowButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,10 @@ export interface IProps {
customReactionImagesEnabled?: boolean;
}

interface IState {
tooltipRendered: boolean;
tooltipVisible: boolean;
}

export default class ReactionsRowButton extends React.PureComponent<IProps, IState> {
export default class ReactionsRowButton extends React.PureComponent<IProps> {
public static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;

public state = {
tooltipRendered: false,
tooltipVisible: false,
};

public onClick = (): void => {
const { mxEvent, myReactionEvent, content } = this.props;
if (myReactionEvent) {
Expand All @@ -74,21 +64,6 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
}
};

public onMouseOver = (): void => {
this.setState({
// To avoid littering the DOM with a tooltip for every reaction,
// only render it on first use.
tooltipRendered: true,
tooltipVisible: true,
});
};

public onMouseLeave = (): void => {
this.setState({
tooltipVisible: false,
});
};

public render(): React.ReactNode {
const { mxEvent, content, count, reactionEvents, myReactionEvent } = this.props;

Expand All @@ -97,19 +72,6 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
mx_ReactionsRowButton_selected: !!myReactionEvent,
});

let tooltip: JSX.Element | undefined;
if (this.state.tooltipRendered) {
tooltip = (
<ReactionsRowButtonTooltip
mxEvent={this.props.mxEvent}
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;
Expand Down Expand Up @@ -156,20 +118,24 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
}

return (
<AccessibleButton
className={classes}
aria-label={label}
onClick={this.onClick}
disabled={this.props.disabled}
onMouseOver={this.onMouseOver}
onMouseLeave={this.onMouseLeave}
<ReactionsRowButtonTooltip
mxEvent={this.props.mxEvent}
content={content}
reactionEvents={reactionEvents}
customReactionImagesEnabled={this.props.customReactionImagesEnabled}
>
{reactionContent}
<span className="mx_ReactionsRowButton_count" aria-hidden="true">
{count}
</span>
{tooltip}
</AccessibleButton>
<AccessibleButton
className={classes}
aria-label={label}
onClick={this.onClick}
disabled={this.props.disabled}
>
{reactionContent}
<span className="mx_ReactionsRowButton_count" aria-hidden="true">
{count}
</span>
</AccessibleButton>
</ReactionsRowButtonTooltip>
);
}
}
44 changes: 12 additions & 32 deletions src/components/views/messages/ReactionsRowButtonTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import React from "react";
import React, { PropsWithChildren } from "react";
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
import { Tooltip } from "@vector-im/compound-web";

import { unicodeToShortcode } from "../../../HtmlUtils";
import { _t } from "../../../languageHandler";
import { formatList } from "../../../utils/FormattingUtils";
import Tooltip from "../elements/Tooltip";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import { REACTION_SHORTCODE_KEY } from "./ReactionsRow";
interface IProps {
Expand All @@ -30,20 +30,18 @@ interface IProps {
content: string;
// A list of Matrix reaction events for this key
reactionEvents: MatrixEvent[];
visible: boolean;
// Whether to render custom image reactions
customReactionImagesEnabled?: boolean;
}

export default class ReactionsRowButtonTooltip extends React.PureComponent<IProps> {
export default class ReactionsRowButtonTooltip extends React.PureComponent<PropsWithChildren<IProps>> {
public static contextType = MatrixClientContext;
public context!: React.ContextType<typeof MatrixClientContext>;

public render(): React.ReactNode {
const { content, reactionEvents, mxEvent, visible } = this.props;
const { content, reactionEvents, mxEvent, children } = this.props;

const room = this.context.getRoom(mxEvent.getRoomId());
let tooltipLabel: JSX.Element | undefined;
if (room) {
const senders: string[] = [];
let customReactionName: string | undefined;
Expand All @@ -57,34 +55,16 @@ export default class ReactionsRowButtonTooltip extends React.PureComponent<IProp
undefined;
}
const shortName = unicodeToShortcode(content) || customReactionName;
tooltipLabel = (
<div>
{_t(
"timeline|reactions|tooltip",
{
shortName,
},
{
reactors: () => {
return <div className="mx_Tooltip_title">{formatList(senders, 6)}</div>;
},
reactedWith: (sub) => {
if (!shortName) {
return null;
}
return <div className="mx_Tooltip_sub">{sub}</div>;
},
},
)}
</div>
);
}
const formattedSenders = formatList(senders, 6);
const caption = shortName ? _t("timeline|reactions|tooltip_caption", { shortName }) : undefined;

let tooltip: JSX.Element | undefined;
if (tooltipLabel) {
tooltip = <Tooltip visible={visible} label={tooltipLabel} />;
return (
<Tooltip label={formattedSenders} caption={caption} placement="right">
{children}
</Tooltip>
);
}

return tooltip;
return children;
}
}
128 changes: 61 additions & 67 deletions src/components/views/rooms/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
THREAD_RELATION_TYPE,
} from "matrix-js-sdk/src/matrix";
import { Optional } from "matrix-events-sdk";
import { Tooltip } from "@vector-im/compound-web";

import { _t } from "../../../languageHandler";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
Expand All @@ -40,7 +41,6 @@ import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import VoiceRecordComposerTile from "./VoiceRecordComposerTile";
import { VoiceRecordingStore } from "../../../stores/VoiceRecordingStore";
import { RecordingState } from "../../../audio/VoiceRecording";
import Tooltip, { Alignment } from "../elements/Tooltip";
import ResizeNotifier from "../../../utils/ResizeNotifier";
import { E2EStatus } from "../../../utils/ShieldUtils";
import SendMessageComposer, { SendMessageComposer as SendMessageComposerClass } from "./SendMessageComposer";
Expand Down Expand Up @@ -110,7 +110,6 @@ interface IState {
}

export class MessageComposer extends React.Component<IProps, IState> {
private tooltipId = `mx_MessageComposer_${Math.random()}`;
private dispatcherRef?: string;
private messageComposerInput = createRef<SendMessageComposerClass>();
private voiceRecordingButton = createRef<VoiceRecordComposerTile>();
Expand Down Expand Up @@ -568,12 +567,9 @@ export class MessageComposer extends React.Component<IProps, IState> {
}

let recordingTooltip: JSX.Element | undefined;
if (this.state.recordingTimeLeftSeconds) {
const secondsLeft = Math.round(this.state.recordingTimeLeftSeconds);
recordingTooltip = (
<Tooltip id={this.tooltipId} label={formatTimeLeft(secondsLeft)} alignment={Alignment.Top} />
);
}

const isTooltipOpen = Boolean(this.state.recordingTimeLeftSeconds);
const secondsLeft = this.state.recordingTimeLeftSeconds ? Math.round(this.state.recordingTimeLeftSeconds) : 0;

const threadId =
this.props.relation?.rel_type === THREAD_RELATION_TYPE.name ? this.props.relation.event_id : null;
Expand All @@ -599,68 +595,66 @@ export class MessageComposer extends React.Component<IProps, IState> {
});

return (
<div
className={classes}
ref={this.ref}
aria-describedby={this.state.recordingTimeLeftSeconds ? this.tooltipId : undefined}
role="region"
aria-label={_t("a11y|message_composer")}
>
{recordingTooltip}
<div className="mx_MessageComposer_wrapper">
<ReplyPreview
replyToEvent={this.props.replyToEvent}
permalinkCreator={this.props.permalinkCreator}
/>
<div className="mx_MessageComposer_row">
{e2eIcon}
{composer}
<div className="mx_MessageComposer_actions">
{controls}
{canSendMessages && (
<MessageComposerButtons
addEmoji={this.addEmoji}
haveRecording={this.state.haveRecording}
isMenuOpen={this.state.isMenuOpen}
isStickerPickerOpen={this.state.isStickerPickerOpen}
menuPosition={menuPosition}
relation={this.props.relation}
onRecordStartEndClick={this.onRecordStartEndClick}
setStickerPickerOpen={this.setStickerPickerOpen}
showLocationButton={
!window.electron && SettingsStore.getValue(UIFeature.LocationSharing)
}
showPollsButton={this.state.showPollsButton}
showStickersButton={this.showStickersButton}
isRichTextEnabled={this.state.isRichTextEnabled}
onComposerModeClick={this.onRichTextToggle}
toggleButtonMenu={this.toggleButtonMenu}
showVoiceBroadcastButton={this.state.showVoiceBroadcastButton}
onStartVoiceBroadcastClick={() => {
setUpVoiceBroadcastPreRecording(
this.props.room,
MatrixClientPeg.safeGet(),
SdkContextClass.instance.voiceBroadcastPlaybacksStore,
SdkContextClass.instance.voiceBroadcastRecordingsStore,
SdkContextClass.instance.voiceBroadcastPreRecordingStore,
);
this.toggleButtonMenu();
}}
/>
)}
{showSendButton && (
<SendButton
key="controls_send"
onClick={this.sendMessage}
title={
this.state.haveRecording ? _t("composer|send_button_voice_message") : undefined
}
/>
)}
<Tooltip open={isTooltipOpen} label={formatTimeLeft(secondsLeft)} placement="top">
<div className={classes} ref={this.ref} role="region" aria-label={_t("a11y|message_composer")}>
{recordingTooltip}
<div className="mx_MessageComposer_wrapper">
<ReplyPreview
replyToEvent={this.props.replyToEvent}
permalinkCreator={this.props.permalinkCreator}
/>
<div className="mx_MessageComposer_row">
{e2eIcon}
{composer}
<div className="mx_MessageComposer_actions">
{controls}
{canSendMessages && (
<MessageComposerButtons
addEmoji={this.addEmoji}
haveRecording={this.state.haveRecording}
isMenuOpen={this.state.isMenuOpen}
isStickerPickerOpen={this.state.isStickerPickerOpen}
menuPosition={menuPosition}
relation={this.props.relation}
onRecordStartEndClick={this.onRecordStartEndClick}
setStickerPickerOpen={this.setStickerPickerOpen}
showLocationButton={
!window.electron && SettingsStore.getValue(UIFeature.LocationSharing)
}
showPollsButton={this.state.showPollsButton}
showStickersButton={this.showStickersButton}
isRichTextEnabled={this.state.isRichTextEnabled}
onComposerModeClick={this.onRichTextToggle}
toggleButtonMenu={this.toggleButtonMenu}
showVoiceBroadcastButton={this.state.showVoiceBroadcastButton}
onStartVoiceBroadcastClick={() => {
setUpVoiceBroadcastPreRecording(
this.props.room,
MatrixClientPeg.safeGet(),
SdkContextClass.instance.voiceBroadcastPlaybacksStore,
SdkContextClass.instance.voiceBroadcastRecordingsStore,
SdkContextClass.instance.voiceBroadcastPreRecordingStore,
);
this.toggleButtonMenu();
}}
/>
)}
{showSendButton && (
<SendButton
key="controls_send"
onClick={this.sendMessage}
title={
this.state.haveRecording
? _t("composer|send_button_voice_message")
: undefined
}
/>
)}
</div>
</div>
</div>
</div>
</div>
</Tooltip>
);
}
}
Expand Down
Loading

0 comments on commit 88e8e2d

Please sign in to comment.