fix copy message with styles

This commit is contained in:
Pavel Mineev
2021-05-30 12:50:37 -05:00
committed by Umputun
parent c6e2c38e34
commit 79eab8ccde
2 changed files with 41 additions and 42 deletions
+28 -25
View File
@@ -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;
}
+13 -17
View File
@@ -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<CommentProps, State> {
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(`<b>${username}</b>&nbsp;${time}<br>${text.replace(/\n+/g, '<br>')}`);
@@ -692,7 +696,7 @@ export class Comment extends Component<CommentProps, State> {
)}
<a href={`${o.locator.url}#${COMMENT_NODE_CLASSNAME_PREFIX}${o.id}`} className="comment__time">
<FormatTime time={o.time} />
{getLocalDatetime(this.props.intl, o.time)}
</a>
{!!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 (
<FormattedMessage
id="comment.time"
defaultMessage="{day} at {time}"
values={{
day: intl.formatDate(time),
time: intl.formatTime(time),
}}
/>
);
function getLocalDatetime(intl: IntlShape, date: Date) {
return intl.formatMessage(messages.commentTime, {
day: intl.formatDate(date),
time: intl.formatTime(date),
});
}