mirror of
https://github.com/danog/telegram-tt.git
synced 2025-01-22 05:11:55 +01:00
Text Formatter: Full support for spoilers
This commit is contained in:
parent
c12b58a9f1
commit
616a266cc4
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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"
|
||||
|
@ -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(/ /g, ' ');
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user