From 79eab8ccde4074ab0adaa8daee09416f4f8671b6 Mon Sep 17 00:00:00 2001 From: Pavel Mineev Date: Wed, 26 May 2021 15:08:39 +0300 Subject: [PATCH] fix copy message with styles --- frontend/app/common/copy.ts | 53 +++++++++++---------- frontend/app/components/comment/comment.tsx | 30 +++++------- 2 files changed, 41 insertions(+), 42 deletions(-) diff --git a/frontend/app/common/copy.ts b/frontend/app/common/copy.ts index 188a603e..8c9462b4 100644 --- a/frontend/app/common/copy.ts +++ b/frontend/app/common/copy.ts @@ -1,43 +1,46 @@ -// based on https://github.com/sindresorhus/copy-text-to-clipboard, but improved to copy text styles too -export function copy(input: string): boolean { - const element = document.createElement('textarea') as HTMLTextAreaElement; +export function copy(content: string): boolean { + // We use `div` instead of `input` or `textarea` because we want to copy styles + const container = document.createElement('div'); const previouslyFocusedElement = document.activeElement as HTMLElement; - element.value = input; + container.innerHTML = content; - // Prevent keyboard from showing on mobile - element.setAttribute('readonly', ''); - - Object.assign(element.style, { + Object.assign(container.style, { contain: 'strict', position: 'absolute', left: '-9999px', fontSize: '12pt', // Prevent zooming on iOS }); - const selection = document.getSelection(); - let originalRange: boolean | Range = false; + document.body.appendChild(container); - if (selection && selection.rangeCount > 0) { - originalRange = selection.getRangeAt(0); + let selection = window.getSelection(); + // save original selection + const originalRange = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null; + const range = document.createRange(); + + range.selectNodeContents(container); + + if (selection) { + selection.removeAllRanges(); + selection.addRange(range); } - document.body.append(element); - element.select(); + document.execCommand('copy'); - // Explicit selection workaround for iOS - element.selectionStart = 0; - element.selectionEnd = input.length; - - let isSuccess = false; + let success = false; try { - isSuccess = document.execCommand('copy'); - } catch (_) {} + success = document.execCommand('copy'); + } catch (err) {} - element.remove(); - - if (selection && originalRange) { + if (selection) { selection.removeAllRanges(); + } + + document.body.removeChild(container); + + // Put the selection back in case had it before + if (originalRange && selection) { selection.addRange(originalRange); } @@ -46,5 +49,5 @@ export function copy(input: string): boolean { previouslyFocusedElement.focus(); } - return isSuccess; + return success; } diff --git a/frontend/app/components/comment/comment.tsx b/frontend/app/components/comment/comment.tsx index 3ed409fa..69ed9de9 100644 --- a/frontend/app/components/comment/comment.tsx +++ b/frontend/app/components/comment/comment.tsx @@ -17,7 +17,7 @@ import { Button } from 'components/button'; import { Countdown } from 'components/countdown'; import { getPreview, uploadImage } from 'common/api'; import { postMessage } from 'utils/postMessage'; -import { FormattedMessage, useIntl, IntlShape, defineMessages } from 'react-intl'; +import { FormattedMessage, IntlShape, defineMessages } from 'react-intl'; import { getVoteMessage, VoteMessagesTypes } from './getVoteMessage'; import { getBlockingDurations } from './getBlockingDurations'; import { boundActions } from './connected-comment'; @@ -85,6 +85,10 @@ const messages = defineMessages({ id: 'comment.expired-time', defaultMessage: 'Editing time has expired.', }, + commentTime: { + id: 'comment.time', + defaultMessage: '{day} at {time}', + }, }); export type CommentProps = { @@ -372,8 +376,8 @@ export class Comment extends Component { copyComment = () => { const username = this.props.data.user.name; - const time = this.props.data.time; - const text = this.textNode.current!.textContent || ''; + const time = getLocalDatetime(this.props.intl, new Date(this.props.data.time)); + const text = this.textNode.current?.textContent || ''; copy(`${username} ${time}
${text.replace(/\n+/g, '
')}`); @@ -692,7 +696,7 @@ export class Comment extends Component { )} - + {getLocalDatetime(this.props.intl, o.time)} {!!props.level && props.level > 0 && props.view === 'main' && ( @@ -881,17 +885,9 @@ function getTextSnippet(html: string) { return snippet.length === LENGTH && result.length !== LENGTH ? `${snippet}...` : snippet; } -function FormatTime({ time }: { time: Date }) { - const intl = useIntl(); - - return ( - - ); +function getLocalDatetime(intl: IntlShape, date: Date) { + return intl.formatMessage(messages.commentTime, { + day: intl.formatDate(date), + time: intl.formatTime(date), + }); }