Text Formatter: Full support for spoilers

This commit is contained in:
Alexander Zinchuk 2022-01-28 02:11:09 +01:00
parent c12b58a9f1
commit 616a266cc4
4 changed files with 56 additions and 15 deletions

View File

@ -18,7 +18,9 @@ import SafeLink from '../SafeLink';
import Spoiler from '../spoiler/Spoiler';
import { LangFn } from '../../../hooks/useLang';
export type TextPart = string | Element;
export type TextPart =
string
| Element;
export function renderMessageSummary(
lang: LangFn,
@ -500,7 +502,10 @@ function processEntityAsHtml(
dir="auto"
>${renderedContent}</a>`;
case ApiMessageEntityTypes.Spoiler:
return `||${renderedContent}||`;
return `<span
class="spoiler"
data-entity-type="${ApiMessageEntityTypes.Spoiler}"
>${renderedContent}</span>`;
default:
return renderedContent;
}

View File

@ -399,6 +399,18 @@
}
}
.spoiler {
background-image: url('../../../assets/spoiler-dots-black.png');
background-size: auto min(100%, 1.125rem);
border-radius: 0.5rem;
padding: 0 0.3125rem 0.125rem 0.3125rem;
text-shadow: -2px -2px 0 var(--color-background), 2px -2px 0 var(--color-background), -2px 2px 0 var(--color-background), 2px 2px 0 var(--color-background);
}
html.theme-dark & .spoiler {
background-image: url('../../../assets/spoiler-dots-white.png');
}
.clone {
position: absolute;
top: 0;

View File

@ -3,6 +3,7 @@ import React, {
} from '../../../lib/teact/teact';
import { IAnchorPosition } from '../../../types';
import { ApiMessageEntityTypes } from '../../../api/types';
import { EDITABLE_INPUT_ID } from '../../../config';
import buildClassName from '../../../util/buildClassName';
@ -32,6 +33,7 @@ interface ISelectedTextFormats {
underline?: boolean;
strikethrough?: boolean;
monospace?: boolean;
spoiler?: boolean;
}
const TEXT_FORMAT_BY_TAG_NAME: Record<string, keyof ISelectedTextFormats> = {
@ -42,6 +44,7 @@ const TEXT_FORMAT_BY_TAG_NAME: Record<string, keyof ISelectedTextFormats> = {
U: 'underline',
DEL: 'strikethrough',
CODE: 'monospace',
SPAN: 'spoiler',
};
const fragmentEl = document.createElement('div');
@ -188,6 +191,34 @@ const TextFormatter: FC<OwnProps> = ({
return undefined;
}
const handleSpoilerText = useCallback(() => {
if (selectedTextFormats.spoiler) {
const element = getSelectedElement();
if (
!selectedRange
|| !element
|| element.dataset.entityType !== ApiMessageEntityTypes.Spoiler
|| !element.textContent
) {
return;
}
element.replaceWith(element.textContent);
setSelectedTextFormats((selectedFormats) => ({
...selectedFormats,
spoiler: false,
}));
return;
}
const text = getSelectedText();
document.execCommand(
'insertHTML', false, `<span class="spoiler" data-entity-type="${ApiMessageEntityTypes.Spoiler}">${text}</span>`,
);
onClose();
}, [getSelectedElement, getSelectedText, onClose, selectedRange, selectedTextFormats.spoiler]);
const handleBoldText = useCallback(() => {
setSelectedTextFormats((selectedFormats) => {
// Somehow re-applying 'bold' command to already bold text doesn't work
@ -249,16 +280,9 @@ const TextFormatter: FC<OwnProps> = ({
document.execCommand('insertHTML', false, `<del>${text}</del>`);
onClose();
}, [
getSelectedElement, getSelectedText, onClose,
selectedRange, selectedTextFormats.strikethrough,
getSelectedElement, getSelectedText, onClose, selectedRange, selectedTextFormats.strikethrough,
]);
const handleSpoilerText = useCallback(() => {
const text = getSelectedText();
document.execCommand('insertHTML', false, `||${text}||`);
onClose();
}, [getSelectedText, onClose]);
const handleMonospaceText = useCallback(() => {
if (selectedTextFormats.monospace) {
const element = getSelectedElement();
@ -276,6 +300,7 @@ const TextFormatter: FC<OwnProps> = ({
...selectedFormats,
monospace: false,
}));
return;
}
@ -283,8 +308,7 @@ const TextFormatter: FC<OwnProps> = ({
document.execCommand('insertHTML', false, `<code class="text-entity-code" dir="auto">${text}</code>`);
onClose();
}, [
getSelectedElement, getSelectedText, onClose,
selectedRange, selectedTextFormats.monospace,
getSelectedElement, getSelectedText, onClose, selectedRange, selectedTextFormats.monospace,
]);
function handleLinkUrlConfirm() {
@ -299,6 +323,7 @@ const TextFormatter: FC<OwnProps> = ({
(element as HTMLAnchorElement).href = formattedLinkUrl;
onClose();
return;
}
@ -389,10 +414,12 @@ const TextFormatter: FC<OwnProps> = ({
<Button
color="translucent"
ariaLabel="Spoiler text"
className={getFormatButtonClassName('spoiler')}
onClick={handleSpoilerText}
>
<i className="icon-eye-closed" />
</Button>
<div className="TextFormatter-divider" />
<Button
color="translucent"
ariaLabel="Bold text"

View File

@ -60,9 +60,6 @@ function parseMarkdown(html: string) {
parsedHtml = parsedHtml.replace(/<img[^>]+alt="([^"]+)"[^>]*>/gm, '$1');
}
// Strip redundant <span> tags
parsedHtml = parsedHtml.replace(/<\/?span([^>]*)?>/g, '');
// Strip redundant nbsp's
parsedHtml = parsedHtml.replace(/&nbsp;/g, ' ');