diff --git a/res/css/_components.scss b/res/css/_components.scss index 985987ec2e2..2ba049eada5 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -133,6 +133,7 @@ @import "./views/elements/_AccessibleButton.scss"; @import "./views/elements/_AddressSelector.scss"; @import "./views/elements/_AddressTile.scss"; +@import "./views/elements/_CopyableText.scss"; @import "./views/elements/_DesktopBuildsNotice.scss"; @import "./views/elements/_DesktopCapturerSourcePicker.scss"; @import "./views/elements/_DialPadBackspaceButton.scss"; diff --git a/res/css/views/elements/_CopyableText.scss b/res/css/views/elements/_CopyableText.scss new file mode 100644 index 00000000000..690db0e24c8 --- /dev/null +++ b/res/css/views/elements/_CopyableText.scss @@ -0,0 +1,47 @@ +/* +Copyright 2019 New Vector Ltd +Copyright 2022 Šimon Brandner + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_CopyableText { + display: flex; + border-radius: 5px; + border: solid 1px $light-fg-color; + margin-bottom: 10px; + margin-top: 10px; + padding: 10px; + width: max-content; + max-width: 100%; + + .mx_CopyableText_copyButton { + flex-shrink: 0; + width: 20px; + height: 20px; + cursor: pointer; + margin-left: 20px; + display: block; + + &::before { + content: ""; + + mask-image: url($copy-button-url); + background-color: $message-action-bar-fg-color; + width: 20px; + height: 20px; + display: block; + background-repeat: no-repeat; + } + } +} diff --git a/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss index 3e61e80a9d3..3779162223b 100644 --- a/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss @@ -1,5 +1,6 @@ /* Copyright 2019 New Vector Ltd +Copyright 2022 Šimon Brandner Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,34 +28,3 @@ limitations under the License. word-break: break-all; user-select: all; } - -.mx_HelpUserSettingsTab_copy { - display: flex; - border-radius: 5px; - border: solid 1px $light-fg-color; - margin-bottom: 10px; - margin-top: 10px; - padding: 10px; - width: max-content; - max-width: 100%; - - .mx_HelpUserSettingsTab_copyButton { - flex-shrink: 0; - width: 20px; - height: 20px; - cursor: pointer; - margin-left: 20px; - display: block; - - &::before { - content: ""; - - mask-image: url($copy-button-url); - background-color: $message-action-bar-fg-color; - width: 20px; - height: 20px; - display: block; - background-repeat: no-repeat; - } - } -} diff --git a/src/components/views/elements/CopyableText.tsx b/src/components/views/elements/CopyableText.tsx new file mode 100644 index 00000000000..14ff5fadd02 --- /dev/null +++ b/src/components/views/elements/CopyableText.tsx @@ -0,0 +1,63 @@ +/* +Copyright 2019-2022 The Matrix.org Foundation C.I.C. +Copyright 2022 Šimon Brandner + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { useEffect, useRef } from "react"; + +import { _t } from "../../../languageHandler"; +import { copyPlaintext } from "../../../utils/strings"; +import { toRightOf, createMenu } from "../../structures/ContextMenu"; +import GenericTextContextMenu from "../context_menus/GenericTextContextMenu"; +import { ButtonEvent } from "./AccessibleButton"; +import AccessibleTooltipButton from "./AccessibleTooltipButton"; + +interface IProps { + children: React.ReactNode; + getTextToCopy: () => string; +} + +const CopyableText: React.FC = ({ children, getTextToCopy }) => { + const closeCopiedTooltip = useRef<() => void>(); + const divRef = useRef(); + + useEffect(() => () => { + if (closeCopiedTooltip.current) closeCopiedTooltip.current(); + }, [closeCopiedTooltip]); + + const onCopyClickInternal = async (e: ButtonEvent) => { + e.preventDefault(); + const target = e.target as HTMLDivElement; // copy target before we go async and React throws it away + + const successful = await copyPlaintext(getTextToCopy()); + const buttonRect = target.getBoundingClientRect(); + const { close } = createMenu(GenericTextContextMenu, { + ...toRightOf(buttonRect, 2), + message: successful ? _t('Copied!') : _t('Failed to copy'), + }); + closeCopiedTooltip.current = target.onmouseleave = close; + }; + + return
+ { children } + +
; +}; + +export default CopyableText; diff --git a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx index a2b8166f6c7..12d94a15a44 100644 --- a/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/AdvancedRoomSettingsTab.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019 - 2021 The Matrix.org Foundation C.I.C. +Copyright 2019 - 2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import Modal from "../../../../../Modal"; import dis from "../../../../../dispatcher/dispatcher"; import { Action } from '../../../../../dispatcher/actions'; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; +import CopyableText from "../../../elements/CopyableText"; interface IProps { roomId: string; @@ -149,8 +150,10 @@ export default class AdvancedRoomSettingsTab extends React.Component
- { _t("Internal room ID:") }  - { this.props.roomId } + { _t("Internal room ID") } + this.props.roomId}> + { this.props.roomId } +
{ unfederatableSection } diff --git a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx index a2de7e81a96..5b744fc0f6f 100644 --- a/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/HelpUserSettingsTab.tsx @@ -1,5 +1,5 @@ /* -Copyright 2019-2021 The Matrix.org Foundation C.I.C. +Copyright 2019-2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,25 +17,21 @@ limitations under the License. import React from 'react'; import { logger } from "matrix-js-sdk/src/logger"; -import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton"; +import AccessibleButton from "../../../elements/AccessibleButton"; import { _t, getCurrentLanguage } from "../../../../../languageHandler"; import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; -import AccessibleTooltipButton from '../../../elements/AccessibleTooltipButton'; import SdkConfig from "../../../../../SdkConfig"; import createRoom from "../../../../../createRoom"; import Modal from "../../../../../Modal"; import PlatformPeg from "../../../../../PlatformPeg"; import UpdateCheckButton from "../../UpdateCheckButton"; import { replaceableComponent } from "../../../../../utils/replaceableComponent"; -import { copyPlaintext } from "../../../../../utils/strings"; -import * as ContextMenu from "../../../../structures/ContextMenu"; -import { toRightOf } from "../../../../structures/ContextMenu"; import BugReportDialog from '../../../dialogs/BugReportDialog'; -import GenericTextContextMenu from "../../../context_menus/GenericTextContextMenu"; import { OpenToTabPayload } from "../../../../../dispatcher/payloads/OpenToTabPayload"; import { Action } from "../../../../../dispatcher/actions"; import { UserTab } from "../../../dialogs/UserSettingsDialog"; import dis from "../../../../../dispatcher/dispatcher"; +import CopyableText from "../../../elements/CopyableText"; interface IProps { closeSettingsFn: () => void; @@ -48,8 +44,6 @@ interface IState { @replaceableComponent("views.settings.tabs.user.HelpUserSettingsTab") export default class HelpUserSettingsTab extends React.Component { - protected closeCopiedTooltip: () => void; - constructor(props) { super(props); @@ -68,12 +62,6 @@ export default class HelpUserSettingsTab extends React.Component }); } - 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 - if (this.closeCopiedTooltip) this.closeCopiedTooltip(); - } - private getVersionInfo(): { appVersion: string, olmVersion: string } { const brand = SdkConfig.get().brand; const appVersion = this.state.appVersion || 'unknown'; @@ -192,26 +180,9 @@ export default class HelpUserSettingsTab extends React.Component ); } - private async copy(text: string, e: ButtonEvent) { - e.preventDefault(); - const target = e.target as HTMLDivElement; // copy target before we go async and React throws it away - - const successful = await copyPlaintext(text); - const buttonRect = target.getBoundingClientRect(); - const { close } = ContextMenu.createMenu(GenericTextContextMenu, { - ...toRightOf(buttonRect, 2), - message: successful ? _t('Copied!') : _t('Failed to copy'), - }); - this.closeCopiedTooltip = target.onmouseleave = close; - } - - private onAccessTokenCopyClick = (e: ButtonEvent) => { - this.copy(MatrixClientPeg.get().getAccessToken(), e); - }; - - private onCopyVersionClicked = (e: ButtonEvent) => { + private getVersionTextToCopy = (): string => { const { appVersion, olmVersion } = this.getVersionInfo(); - this.copy(`${appVersion}\n${olmVersion}`, e); + return `${appVersion}\n${olmVersion}`; }; private onKeyboardShortcutsClicked = (): void => { @@ -324,15 +295,10 @@ export default class HelpUserSettingsTab extends React.Component
{ _t("Versions") }
-
+ { appVersion }
{ olmVersion }
- -
+ { updateButton }
@@ -348,14 +314,9 @@ export default class HelpUserSettingsTab extends React.Component { _t("Access Token") }
{ _t("Your access token gives full access to your account." + " Do not share it with anyone.") } -
- { MatrixClientPeg.get().getAccessToken() } - -
+ MatrixClientPeg.get().getAccessToken()}> + { MatrixClientPeg.get().getAccessToken() } +
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 83d550a60b5..c3b93e83443 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1425,7 +1425,6 @@ "FAQ": "FAQ", "Keyboard Shortcuts": "Keyboard Shortcuts", "Versions": "Versions", - "Copy": "Copy", "Homeserver is": "Homeserver is", "Identity server is": "Identity server is", "Access Token": "Access Token", @@ -1547,7 +1546,7 @@ "this room": "this room", "View older messages in %(roomName)s.": "View older messages in %(roomName)s.", "Space information": "Space information", - "Internal room ID:": "Internal room ID:", + "Internal room ID": "Internal room ID", "Room version": "Room version", "Room version:": "Room version:", "Developer options": "Developer options", @@ -2220,6 +2219,7 @@ "Error loading Widget": "Error loading Widget", "Error - Mixed content": "Error - Mixed content", "Popout widget": "Popout widget", + "Copy": "Copy", "Message search initialisation failed, check your settings for more information": "Message search initialisation failed, check your settings for more information", "Use the Desktop app to see all encrypted files": "Use the Desktop app to see all encrypted files", "Use the Desktop app to search encrypted messages": "Use the Desktop app to search encrypted messages",