Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update new ShareDialog #28595

Closed
wants to merge 1 commit into from
Closed
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
36 changes: 30 additions & 6 deletions res/css/views/dialogs/_ShareDialog.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Please see LICENSE files in the repository root for full details.

.mx_ShareDialog .mx_ShareDialog_content {
margin: 10px 0;

text-align: center;
.mx_CopyableText {
width: unset; /* full width */

Expand All @@ -27,27 +27,51 @@ Please see LICENSE files in the repository root for full details.
}
}

.mx_ShareDialog_split {
.mx_ShareDialog_url {
text-align: center;
color: var(--cpd-color-text-secondary);
overflow-wrap: break-word;
max-width: 400px;
}
.mx_ShareDialog_checkbox {
display: inline-block;
margin: var(--cpd-space-4x) 0;
}
.mx_ShareDialog_button {
width: 100%;
}

.mx_ShareDialog_qrCode {
display: flex;
justify-content: center;
}

.mx_ShareDialog_qrCode img {
margin: 0 var(--cpd-space-8x);
image-rendering: pixelated;
}

.mx_LegacyShareDialog_split {
display: flex;
flex-wrap: wrap;
}

.mx_ShareDialog_qrcode_container {
.mx_LegacyShareDialog_qrcode_container {
float: left;
height: 256px;
width: 256px;
margin-right: 64px;
}

.mx_ShareDialog_qrcode_container + .mx_ShareDialog_social_container {
.mx_LegacyShareDialog_qrcode_container + .mx_LegacyShareDialog_social_container {
width: 299px;
}

.mx_ShareDialog_social_container {
.mx_LegacyShareDialog_social_container {
display: inline-block;
}

.mx_ShareDialog_social_icon {
.mx_LegacyShareDialog_social_icon {
display: inline-grid;
margin-right: 10px;
margin-bottom: 10px;
Expand Down
212 changes: 171 additions & 41 deletions src/components/views/dialogs/ShareDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,20 @@ Please see LICENSE files in the repository root for full details.

import * as React from "react";
import { Room, RoomMember, MatrixEvent, User } from "matrix-js-sdk/src/matrix";
import { Button, Text } from "@vector-im/compound-web";
import LinkIcon from "@vector-im/compound-design-tokens/assets/web/icons/link";

import { _t } from "../../../languageHandler";
import QRCode from "../elements/QRCode";
import { RoomPermalinkCreator, makeUserPermalink } from "../../../utils/permalinks/Permalinks";
import { selectText } from "../../../utils/strings";
import { copyPlaintext, selectText } from "../../../utils/strings";
import StyledCheckbox from "../elements/StyledCheckbox";
import SettingsStore from "../../../settings/SettingsStore";
import { UIFeature } from "../../../settings/UIFeature";
import BaseDialog from "./BaseDialog";
import CopyableText from "../elements/CopyableText";
import { XOR } from "../../../@types/common";

/* eslint-disable @typescript-eslint/no-require-imports */
const socials = [
{
name: "Facebook",
img: require("../../../../res/img/social/facebook.png"),
url: (url: string) => `https://www.facebook.com/sharer/sharer.php?u=${url}`,
},
{
name: "Twitter",
img: require("../../../../res/img/social/twitter-2.png"),
url: (url: string) => `https://twitter.com/home?status=${url}`,
},
/* // icon missing
name: 'Google Plus',
img: 'img/social/',
url: (url) => `https://plus.google.com/share?url=${url}`,
},*/ {
name: "LinkedIn",
img: require("../../../../res/img/social/linkedin.png"),
url: (url: string) => `https://www.linkedin.com/shareArticle?mini=true&url=${url}`,
},
{
name: "Reddit",
img: require("../../../../res/img/social/reddit.png"),
url: (url: string) => `https://www.reddit.com/submit?url=${url}`,
},
{
name: "email",
img: require("../../../../res/img/social/email-1.png"),
url: (url: string) => `mailto:?body=${url}`,
},
];
/* eslint-enable @typescript-eslint/no-require-imports */
import { ButtonEvent } from "../elements/AccessibleButton";
import CopyableText from "../elements/CopyableText";

interface BaseProps {
/**
Expand Down Expand Up @@ -91,7 +60,168 @@ interface IState {
permalinkCreator: RoomPermalinkCreator | null;
}

export default class ShareDialog extends React.PureComponent<XOR<Props, EventProps>, IState> {
// TODO: make this a Rect.FC
export class ShareDialog extends React.PureComponent<XOR<Props, EventProps>, IState> {
public constructor(props: XOR<Props, EventProps>) {
super(props);

let permalinkCreator: RoomPermalinkCreator | null = null;
if (props.target instanceof Room) {
permalinkCreator = new RoomPermalinkCreator(props.target);
permalinkCreator.load();
}

this.state = {
// MatrixEvent defaults to share linkSpecificEvent
linkSpecificEvent: this.props.target instanceof MatrixEvent,
permalinkCreator,
};
}

public static onLinkClick(e: React.MouseEvent): void {
e.preventDefault();
selectText(e.currentTarget);
}

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

private getUrl(): string {
if (this.props.target instanceof URL) {
return this.props.target.toString();
} else if (this.props.target instanceof Room) {
if (this.state.linkSpecificEvent) {
const events = this.props.target.getLiveTimeline().getEvents();
return this.state.permalinkCreator!.forEvent(events[events.length - 1].getId()!);
} else {
return this.state.permalinkCreator!.forShareableRoom();
}
} else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
return makeUserPermalink(this.props.target.userId);
} else if (this.state.linkSpecificEvent) {
return this.props.permalinkCreator!.forEvent(this.props.target.getId()!);
} else {
return this.props.permalinkCreator!.forShareableRoom();
}
}

public render(): React.ReactNode {
let title: string | undefined;
let checkbox: JSX.Element | undefined;

if (this.props.target instanceof URL) {
title = this.props.customTitle ?? _t("share|title_link");
} else if (this.props.target instanceof Room) {
title = this.props.customTitle ?? _t("share|title_room");

const events = this.props.target.getLiveTimeline().getEvents();
if (events.length > 0) {
checkbox = (
<div className="mx_ShareDialog_checkbox">
<StyledCheckbox
checked={this.state.linkSpecificEvent}
onChange={this.onLinkSpecificEventCheckboxClick}
>
{_t("share|permalink_most_recent")}
</StyledCheckbox>
</div>
);
}
} else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
title = this.props.customTitle ?? _t("share|title_user");
} else if (this.props.target instanceof MatrixEvent) {
title = this.props.customTitle ?? _t("share|title_message");
checkbox = (
<div>
<StyledCheckbox
checked={this.state.linkSpecificEvent}
onChange={this.onLinkSpecificEventCheckboxClick}
>
{_t("share|permalink_message")}
</StyledCheckbox>
</div>
);
}

const matrixToUrl = this.getUrl();

const showQrCode = SettingsStore.getValue(UIFeature.ShareQRCode);
const onButtonClick = async (e: ButtonEvent): Promise<void> => {
e.preventDefault();

await copyPlaintext(matrixToUrl);
this.props.onFinished();
};

return (
<BaseDialog
title={title}
fixedWidth={false}
className="mx_ShareDialog"
contentId="mx_Dialog_content"
onFinished={this.props.onFinished}
>
{this.props.subtitle && <p>{this.props.subtitle}</p>}
<div className="mx_ShareDialog_content">
{showQrCode && <QRCode className="mx_ShareDialog_qrCode" data={matrixToUrl} />}
<Text className="mx_ShareDialog_url" size="sm" weight="semibold">
{matrixToUrl}
</Text>
{checkbox}
<Button
className="mx_ShareDialog_button mx_Dialog_nonDialogButton"
Icon={LinkIcon}
onClick={onButtonClick}
data-testid="modal_inviteLink"
>
{_t("action|copy_link")}
</Button>
</div>
</BaseDialog>
);
}
}

// LEGACY SHARE DIALOG

/* eslint-disable @typescript-eslint/no-require-imports */
const socials = [
{
name: "Facebook",
img: require("../../../../res/img/social/facebook.png"),
url: (url: string) => `https://www.facebook.com/sharer/sharer.php?u=${url}`,
},
{
name: "Twitter",
img: require("../../../../res/img/social/twitter-2.png"),
url: (url: string) => `https://twitter.com/home?status=${url}`,
},
/* // icon missing
name: 'Google Plus',
img: 'img/social/',
url: (url) => `https://plus.google.com/share?url=${url}`,
},*/ {
name: "LinkedIn",
img: require("../../../../res/img/social/linkedin.png"),
url: (url: string) => `https://www.linkedin.com/shareArticle?mini=true&url=${url}`,
},
{
name: "Reddit",
img: require("../../../../res/img/social/reddit.png"),
url: (url: string) => `https://www.reddit.com/submit?url=${url}`,
},
{
name: "email",
img: require("../../../../res/img/social/email-1.png"),
url: (url: string) => `mailto:?body=${url}`,
},
];
/* eslint-enable @typescript-eslint/no-require-imports */

export default class LegacyShareDialog extends React.PureComponent<XOR<Props, EventProps>, IState> {
public constructor(props: XOR<Props, EventProps>) {
super(props);

Expand Down Expand Up @@ -187,22 +317,22 @@ export default class ShareDialog extends React.PureComponent<XOR<Props, EventPro
qrSocialSection = (
<>
<hr />
<div className="mx_ShareDialog_split">
<div className="mx_LegacyShareDialog_split">
{showQrCode && (
<div className="mx_ShareDialog_qrcode_container">
<div className="mx_LegacyShareDialog_qrcode_container">
<QRCode data={matrixToUrl} width={256} />
</div>
)}
{showSocials && (
<div className="mx_ShareDialog_social_container">
<div className="mx_LegacyShareDialog_social_container">
{socials.map((social) => (
<a
rel="noreferrer noopener"
target="_blank"
key={social.name}
title={social.name}
href={social.url(encodedUrl)}
className="mx_ShareDialog_social_icon"
className="mx_LegacyShareDialog_social_icon"
>
<img src={social.img} alt={social.name} height={64} width={64} />
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { logger } from "matrix-js-sdk/src/logger";
import { EventType, JoinRule, Room } from "matrix-js-sdk/src/matrix";

import Modal from "../../../../Modal";
import ShareDialog from "../../dialogs/ShareDialog";
import { ShareDialog } from "../../dialogs/ShareDialog";
import { _t } from "../../../../languageHandler";
import SettingsStore from "../../../../settings/SettingsStore";
import { calculateRoomVia } from "../../../../utils/permalinks/Permalinks";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ describe("ShareDialog", () => {
return originalGetValue(feature);
});
const { container } = render(<ShareDialog target={room} onFinished={jest.fn()} />, getWrapper());
const qrCodesVisible = container.getElementsByClassName("mx_ShareDialog_qrcode_container").length > 0;
const qrCodesVisible = container.getElementsByClassName("mx_LegacyShareDialog_qrcode_container").length > 0;
expect(qrCodesVisible).toBe(true);
});

Expand All @@ -86,7 +86,7 @@ describe("ShareDialog", () => {
return originalGetValue(feature);
});
const { container } = render(<ShareDialog target={room} onFinished={jest.fn()} />, getWrapper());
const qrCodesVisible = container.getElementsByClassName("mx_ShareDialog_social_container").length > 0;
const qrCodesVisible = container.getElementsByClassName("mx_LegacyShareDialog_social_container").length > 0;
expect(qrCodesVisible).toBe(true);
});
it("renders custom title and subtitle", () => {
Expand Down
Loading