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

Reuse CopyableText component in all places it can be #7701

Merged
merged 1 commit into from
Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions res/css/views/dialogs/_InviteDialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,8 @@ limitations under the License.
text-transform: uppercase;
}

.mx_InviteDialog_footer_link {
display: flex;
justify-content: space-between;
border-radius: 4px;
border: solid 1px $light-fg-color;
padding: 8px;
.mx_CopyableText {
width: unset; // full width

> a {
text-decoration: none;
Expand All @@ -144,22 +140,6 @@ limitations under the License.
text-overflow: ellipsis;
}
}

.mx_InviteDialog_footer_link_copy {
flex-shrink: 0;
cursor: pointer;
margin-left: 20px;
display: inherit;

> div {
mask-image: url($copy-button-url);
background-color: $message-action-bar-fg-color;
margin-left: 5px;
width: 20px;
height: 20px;
background-repeat: no-repeat;
}
}
}

.mx_InviteDialog_roomTile {
Expand Down
48 changes: 12 additions & 36 deletions res/css/views/dialogs/_ShareDialog.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,44 +20,20 @@ limitations under the License.
border-color: $light-fg-color;
}

.mx_ShareDialog_content {
.mx_ShareDialog .mx_ShareDialog_content {
margin: 10px 0;
}

.mx_ShareDialog_matrixto {
display: flex;
justify-content: space-between;
border-radius: 5px;
border: solid 1px $light-fg-color;
margin-bottom: 10px;
margin-top: 30px;
padding: 10px;
}

.mx_ShareDialog_matrixto a {
text-decoration: none;
}

.mx_ShareDialog_matrixto_link {
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
}

.mx_ShareDialog_matrixto_copy {
flex-shrink: 0;
cursor: pointer;
margin-left: 20px;
display: inherit;
}
.mx_ShareDialog_matrixto_copy::after {
content: "";
mask-image: url($copy-button-url);
background-color: $message-action-bar-fg-color;
margin-left: 5px;
width: 20px;
height: 20px;
background-repeat: no-repeat;
.mx_CopyableText {
width: unset; // full width

> a {
text-decoration: none;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}

.mx_ShareDialog_split {
Expand Down
1 change: 1 addition & 0 deletions res/css/views/elements/_CopyableText.scss
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.

.mx_CopyableText {
display: flex;
justify-content: space-between;
border-radius: 5px;
border: solid 1px $light-fg-color;
margin-bottom: 10px;
Expand Down
32 changes: 4 additions & 28 deletions src/components/views/dialogs/InviteDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ import { mediaFromMxc } from "../../../customisations/Media";
import { getAddressType } from "../../../UserAddress";
import BaseAvatar from '../avatars/BaseAvatar';
import AccessibleButton, { ButtonEvent } from '../elements/AccessibleButton';
import { compare, copyPlaintext, selectText } from '../../../utils/strings';
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import * as ContextMenu from "../../structures/ContextMenu";
import { toRightOf } from "../../structures/ContextMenu";
import GenericTextContextMenu from "../context_menus/GenericTextContextMenu";
import { compare, selectText } from '../../../utils/strings';
import Field from '../elements/Field';
import TabbedView, { Tab, TabLocation } from '../../structures/TabbedView';
import Dialpad from '../voip/DialPad';
Expand All @@ -73,6 +69,7 @@ import DialPadBackspaceButton from "../elements/DialPadBackspaceButton";
import SpaceStore from "../../../stores/spaces/SpaceStore";
import CallHandler from "../../../CallHandler";
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
import CopyableText from "../elements/CopyableText";

// we have a number of types defined from the Matrix spec which can't reasonably be altered here.
/* eslint-disable camelcase */
Expand Down Expand Up @@ -1310,20 +1307,6 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
selectText(e.target);
}

private onCopyClick = async e => {
e.preventDefault();
const target = e.target; // copy target before we go async and React throws it away

const successful = await copyPlaintext(makeUserPermalink(MatrixClientPeg.get().getUserId()));
const buttonRect = target.getBoundingClientRect();
const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
...toRightOf(buttonRect, 2),
message: successful ? _t("Copied!") : _t("Failed to copy"),
});
// Drop a reference to this close handler for componentWillUnmount
this.closeCopiedTooltip = target.onmouseleave = close;
};

render() {
let spinner = null;
if (this.state.busy) {
Expand Down Expand Up @@ -1409,18 +1392,11 @@ export default class InviteDialog extends React.PureComponent<IInviteDialogProps
const link = makeUserPermalink(MatrixClientPeg.get().getUserId());
footer = <div className="mx_InviteDialog_footer">
<h3>{ _t("Or send invite link") }</h3>
<div className="mx_InviteDialog_footer_link">
<CopyableText getTextToCopy={() => makeUserPermalink(MatrixClientPeg.get().getUserId())}>
<a href={link} onClick={this.onLinkClick}>
{ link }
</a>
<AccessibleTooltipButton
title={_t("Copy")}
onClick={this.onCopyClick}
className="mx_InviteDialog_footer_link_copy"
>
<div />
</AccessibleTooltipButton>
</div>
</CopyableText>
</div>;
} else if (this.props.kind === KIND_INVITE) {
const room = MatrixClientPeg.get()?.getRoom(this.props.roomId);
Expand Down
58 changes: 8 additions & 50 deletions src/components/views/dialogs/ShareDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ limitations under the License.
*/

import * as React from 'react';
import * as PropTypes from 'prop-types';
import { Room } from "matrix-js-sdk/src/models/room";
import { User } from "matrix-js-sdk/src/models/user";
import { Group } from "matrix-js-sdk/src/models/group";
Expand All @@ -26,17 +25,14 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t } from '../../../languageHandler';
import QRCode from "../elements/QRCode";
import { RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink } from "../../../utils/permalinks/Permalinks";
import * as ContextMenu from "../../structures/ContextMenu";
import { toRightOf } from "../../structures/ContextMenu";
import { copyPlaintext, selectText } from "../../../utils/strings";
import { selectText } from "../../../utils/strings";
import StyledCheckbox from '../elements/StyledCheckbox';
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
import { IDialogProps } from "./IDialogProps";
import SettingsStore from "../../../settings/SettingsStore";
import { UIFeature } from "../../../settings/UIFeature";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import BaseDialog from "./BaseDialog";
import GenericTextContextMenu from "../context_menus/GenericTextContextMenu";
import CopyableText from "../elements/CopyableText";

const socials = [
{
Expand Down Expand Up @@ -78,25 +74,11 @@ interface IState {

@replaceableComponent("views.dialogs.ShareDialog")
export default class ShareDialog extends React.PureComponent<IProps, IState> {
static propTypes = {
onFinished: PropTypes.func.isRequired,
target: PropTypes.oneOfType([
PropTypes.instanceOf(Room),
PropTypes.instanceOf(User),
PropTypes.instanceOf(Group),
PropTypes.instanceOf(RoomMember),
PropTypes.instanceOf(MatrixEvent),
]).isRequired,
};

protected closeCopiedTooltip: () => void;

constructor(props) {
super(props);

this.onCopyClick = this.onCopyClick.bind(this);
this.onLinkSpecificEventCheckboxClick = this.onLinkSpecificEventCheckboxClick.bind(this);

let permalinkCreator: RoomPermalinkCreator = null;
if (props.target instanceof Room) {
permalinkCreator = new RoomPermalinkCreator(props.target);
Expand All @@ -115,33 +97,19 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
selectText(e.target);
}

async onCopyClick(e) {
e.preventDefault();
const target = e.target; // copy target before we go async and React throws it away

const successful = await copyPlaintext(this.getUrl());
const buttonRect = target.getBoundingClientRect();
const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
...toRightOf(buttonRect, 2),
message: successful ? _t('Copied!') : _t('Failed to copy'),
});
// Drop a reference to this close handler for componentWillUnmount
this.closeCopiedTooltip = target.onmouseleave = close;
}

onLinkSpecificEventCheckboxClick() {
private onLinkSpecificEventCheckboxClick = () => {
this.setState({
linkSpecificEvent: !this.state.linkSpecificEvent,
});
}
};

componentWillUnmount() {
// if the Copied tooltip is open then get rid of it, there are ways to close the modal which wouldn't close
// the tooltip otherwise, such as pressing Escape or clicking X really quickly
if (this.closeCopiedTooltip) this.closeCopiedTooltip();
}

getUrl() {
private getUrl() {
let matrixToUrl;

if (this.props.target instanceof Room) {
Expand Down Expand Up @@ -238,21 +206,11 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
onFinished={this.props.onFinished}
>
<div className="mx_ShareDialog_content">
<div className="mx_ShareDialog_matrixto">
<a
title={_t('Link to room')}
href={matrixToUrl}
onClick={ShareDialog.onLinkClick}
className="mx_ShareDialog_matrixto_link"
>
<CopyableText getTextToCopy={() => matrixToUrl}>
<a title={_t('Link to room')} href={matrixToUrl} onClick={ShareDialog.onLinkClick}>
{ matrixToUrl }
</a>
<AccessibleTooltipButton
title={_t("Copy")}
onClick={this.onCopyClick}
className="mx_ShareDialog_matrixto_copy"
/>
</div>
</CopyableText>
{ checkbox }
{ qrSocialSection }
</div>
Expand Down