Add emoji button to text story creation

This commit is contained in:
Josh Perez 2022-11-28 13:52:16 -05:00 committed by GitHub
parent d6d53f9d18
commit 77f92b6cc3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 285 additions and 213 deletions

View file

@ -2,7 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import Measure from 'react-measure';
import React, { useEffect, useRef, useState } from 'react';
import React, { forwardRef, useEffect, useRef, useState } from 'react';
import TextareaAutosize from 'react-textarea-autosize';
import classNames from 'classnames';
@ -21,6 +21,7 @@ import {
getBackgroundColor,
} from '../util/getStoryBackground';
import { SECOND } from '../util/durations';
import { useRefMerger } from '../hooks/useRefMerger';
const renderNewLines: RenderTextCallbackType = ({
text: textWithNewLines,
@ -105,207 +106,214 @@ function getTextStyles(
};
}
export function TextAttachment({
disableLinkPreviewPopup,
i18n,
isEditingText,
isThumbnail,
onChange,
onClick,
onRemoveLinkPreview,
textAttachment,
}: PropsType): JSX.Element | null {
const linkPreview = useRef<HTMLDivElement | null>(null);
const [linkPreviewOffsetTop, setLinkPreviewOffsetTop] = useState<
number | undefined
>();
export const TextAttachment = forwardRef<HTMLTextAreaElement, PropsType>(
function TextAttachmentForwarded(
{
disableLinkPreviewPopup,
i18n,
isEditingText,
isThumbnail,
onChange,
onClick,
onRemoveLinkPreview,
textAttachment,
},
forwardedTextEditorRef
): JSX.Element | null {
const linkPreview = useRef<HTMLDivElement | null>(null);
const [linkPreviewOffsetTop, setLinkPreviewOffsetTop] = useState<
number | undefined
>();
const textContent = textAttachment.text || '';
const textContent = textAttachment.text || '';
const textEditorRef = useRef<HTMLTextAreaElement | null>(null);
const refMerger = useRefMerger();
const textEditorRef = useRef<HTMLTextAreaElement | null>(null);
useEffect(() => {
const node = textEditorRef.current;
if (!node) {
return;
}
node.focus();
node.setSelectionRange(node.value.length, node.value.length);
}, [isEditingText]);
useEffect(() => {
setLinkPreviewOffsetTop(undefined);
}, [textAttachment.preview?.url]);
const [isHoveringOverTooltip, setIsHoveringOverTooltip] = useState(false);
function showTooltip() {
if (disableLinkPreviewPopup) {
return;
}
setIsHoveringOverTooltip(true);
setLinkPreviewOffsetTop(linkPreview?.current?.offsetTop);
}
useEffect(() => {
const timeout = setTimeout(() => {
if (!isHoveringOverTooltip) {
setLinkPreviewOffsetTop(undefined);
useEffect(() => {
const node = textEditorRef?.current;
if (!node) {
return;
}
}, 5 * SECOND);
return () => {
clearTimeout(timeout);
node.focus();
node.setSelectionRange(node.value.length, node.value.length);
}, [isEditingText]);
useEffect(() => {
setLinkPreviewOffsetTop(undefined);
}, [textAttachment.preview?.url]);
const [isHoveringOverTooltip, setIsHoveringOverTooltip] = useState(false);
function showTooltip() {
if (disableLinkPreviewPopup) {
return;
}
setIsHoveringOverTooltip(true);
setLinkPreviewOffsetTop(linkPreview?.current?.offsetTop);
}
useEffect(() => {
const timeout = setTimeout(() => {
if (!isHoveringOverTooltip) {
setLinkPreviewOffsetTop(undefined);
}
}, 5 * SECOND);
return () => {
clearTimeout(timeout);
};
}, [isHoveringOverTooltip]);
const storyBackgroundColor = {
background: getBackgroundColor(textAttachment),
};
}, [isHoveringOverTooltip]);
const storyBackgroundColor = {
background: getBackgroundColor(textAttachment),
};
return (
<Measure bounds>
{({ contentRect, measureRef }) => {
const scaleFactor = (contentRect.bounds?.height || 1) / 1280;
return (
<Measure bounds>
{({ contentRect, measureRef }) => {
const scaleFactor = (contentRect.bounds?.height || 1) / 1280;
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div
className="TextAttachment"
onClick={() => {
if (linkPreviewOffsetTop) {
setLinkPreviewOffsetTop(undefined);
}
onClick?.();
}}
onKeyUp={ev => {
if (ev.key === 'Escape' && linkPreviewOffsetTop) {
setLinkPreviewOffsetTop(undefined);
}
}}
ref={measureRef}
style={isThumbnail ? storyBackgroundColor : undefined}
>
{/*
return (
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div
className="TextAttachment"
onClick={() => {
if (linkPreviewOffsetTop) {
setLinkPreviewOffsetTop(undefined);
}
onClick?.();
}}
onKeyUp={ev => {
if (ev.key === 'Escape' && linkPreviewOffsetTop) {
setLinkPreviewOffsetTop(undefined);
}
}}
ref={measureRef}
style={isThumbnail ? storyBackgroundColor : undefined}
>
{/*
The tooltip must be outside of the scaled area, as it should not scale with
the story, but it must be positioned using the scaled offset
*/}
{textAttachment.preview &&
textAttachment.preview.url &&
linkPreviewOffsetTop &&
!isThumbnail && (
<a
className="TextAttachment__preview__tooltip"
href={textAttachment.preview.url}
rel="noreferrer"
style={{
top: linkPreviewOffsetTop * scaleFactor - 89, // minus height of tooltip and some spacing
}}
target="_blank"
>
<div>
<div className="TextAttachment__preview__tooltip__title">
{i18n('TextAttachment__preview__link')}
</div>
<div className="TextAttachment__preview__tooltip__url">
{textAttachment.preview.url}
{textAttachment.preview &&
textAttachment.preview.url &&
linkPreviewOffsetTop &&
!isThumbnail && (
<a
className="TextAttachment__preview__tooltip"
href={textAttachment.preview.url}
rel="noreferrer"
style={{
top: linkPreviewOffsetTop * scaleFactor - 89, // minus height of tooltip and some spacing
}}
target="_blank"
>
<div>
<div className="TextAttachment__preview__tooltip__title">
{i18n('TextAttachment__preview__link')}
</div>
<div className="TextAttachment__preview__tooltip__url">
{textAttachment.preview.url}
</div>
</div>
<div className="TextAttachment__preview__tooltip__arrow" />
</a>
)}
<div
className="TextAttachment__story"
style={{
...(isThumbnail ? {} : storyBackgroundColor),
transform: `scale(${scaleFactor})`,
}}
>
{(textContent || onChange) && (
<div
className={classNames('TextAttachment__text', {
'TextAttachment__text--with-bg': Boolean(
textAttachment.textBackgroundColor
),
})}
style={{
backgroundColor: textAttachment.textBackgroundColor
? getHexFromNumber(textAttachment.textBackgroundColor)
: 'transparent',
}}
>
{onChange ? (
<TextareaAutosize
className="TextAttachment__text__container TextAttachment__text__textarea"
disabled={!isEditingText}
onChange={ev => onChange(ev.currentTarget.value)}
placeholder={i18n('TextAttachment__placeholder')}
ref={refMerger(forwardedTextEditorRef, textEditorRef)}
style={getTextStyles(
textContent,
textAttachment.textForegroundColor,
textAttachment.textStyle,
i18n
)}
value={textContent}
/>
) : (
<div
className="TextAttachment__text__container"
style={getTextStyles(
textContent,
textAttachment.textForegroundColor,
textAttachment.textStyle,
i18n
)}
>
<Emojify
text={textContent}
renderNonEmoji={renderNewLines}
/>
</div>
)}
</div>
<div className="TextAttachment__preview__tooltip__arrow" />
</a>
)}
<div
className="TextAttachment__story"
style={{
...(isThumbnail ? {} : storyBackgroundColor),
transform: `scale(${scaleFactor})`,
}}
>
{(textContent || onChange) && (
<div
className={classNames('TextAttachment__text', {
'TextAttachment__text--with-bg': Boolean(
textAttachment.textBackgroundColor
),
})}
style={{
backgroundColor: textAttachment.textBackgroundColor
? getHexFromNumber(textAttachment.textBackgroundColor)
: 'transparent',
}}
>
{onChange ? (
<TextareaAutosize
className="TextAttachment__text__container TextAttachment__text__textarea"
disabled={!isEditingText}
onChange={ev => onChange(ev.currentTarget.value)}
placeholder={i18n('TextAttachment__placeholder')}
ref={textEditorRef}
style={getTextStyles(
textContent,
textAttachment.textForegroundColor,
textAttachment.textStyle,
i18n
)}
value={textContent}
)}
{textAttachment.preview && textAttachment.preview.url && (
<div
className={classNames('TextAttachment__preview-container', {
'TextAttachment__preview-container--large': Boolean(
textAttachment.preview.title
),
})}
ref={linkPreview}
onBlur={() => setIsHoveringOverTooltip(false)}
onFocus={showTooltip}
onMouseOut={() => setIsHoveringOverTooltip(false)}
onMouseOver={showTooltip}
>
{onRemoveLinkPreview && (
<div className="TextAttachment__preview__remove">
<button
aria-label={i18n(
'Keyboard--remove-draft-link-preview'
)}
type="button"
onClick={onRemoveLinkPreview}
/>
</div>
)}
<StoryLinkPreview
{...textAttachment.preview}
domain={getDomain(String(textAttachment.preview.url))}
forceCompactMode={
getTextSize(textContent) !== TextSize.Large
}
i18n={i18n}
title={textAttachment.preview.title || undefined}
url={textAttachment.preview.url}
/>
) : (
<div
className="TextAttachment__text__container"
style={getTextStyles(
textContent,
textAttachment.textForegroundColor,
textAttachment.textStyle,
i18n
)}
>
<Emojify
text={textContent}
renderNonEmoji={renderNewLines}
/>
</div>
)}
</div>
)}
{textAttachment.preview && textAttachment.preview.url && (
<div
className={classNames('TextAttachment__preview-container', {
'TextAttachment__preview-container--large': Boolean(
textAttachment.preview.title
),
})}
ref={linkPreview}
onBlur={() => setIsHoveringOverTooltip(false)}
onFocus={showTooltip}
onMouseOut={() => setIsHoveringOverTooltip(false)}
onMouseOver={showTooltip}
>
{onRemoveLinkPreview && (
<div className="TextAttachment__preview__remove">
<button
aria-label={i18n('Keyboard--remove-draft-link-preview')}
type="button"
onClick={onRemoveLinkPreview}
/>
</div>
)}
<StoryLinkPreview
{...textAttachment.preview}
domain={getDomain(String(textAttachment.preview.url))}
forceCompactMode={
getTextSize(textContent) !== TextSize.Large
}
i18n={i18n}
title={textAttachment.preview.title || undefined}
url={textAttachment.preview.url}
/>
</div>
)}
</div>
)}
</div>
</div>
</div>
);
}}
</Measure>
);
}
);
}}
</Measure>
);
}
);