pl-fe: Suggest cleaning dirty URLs in compose form
Signed-off-by: mkljczk <git@mkljczk.pl>
This commit is contained in:
@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { spring } from 'react-motion';
|
||||
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
import Stack from 'pl-fe/components/ui/stack';
|
||||
import OptionalMotion from 'pl-fe/features/ui/util/optional-motion';
|
||||
import { useCompose } from 'pl-fe/hooks/use-compose';
|
||||
|
||||
interface IClearLinkSuggestion {
|
||||
composeId: string;
|
||||
handleAccept: (key: string) => void;
|
||||
handleReject: (key: string) => void;
|
||||
}
|
||||
|
||||
const ClearLinkSuggestion = ({
|
||||
composeId,
|
||||
handleAccept,
|
||||
handleReject,
|
||||
}: IClearLinkSuggestion) => {
|
||||
const compose = useCompose(composeId);
|
||||
const suggestion = compose.clear_link_suggestion;
|
||||
|
||||
if (!suggestion) return null;
|
||||
|
||||
return (
|
||||
<OptionalMotion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<Stack space={1} className='rounded border border-solid border-gray-400 bg-transparent px-2.5 py-2 text-xs text-gray-900 dark:border-gray-800 dark:text-white' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||
<span>
|
||||
<FormattedMessage
|
||||
id='compose.clear_link_suggestion.body'
|
||||
defaultMessage='The link {url} likely includes tracking elements used to mark your online activity. They are not required for the URL to work. Do you want to remove them?'
|
||||
values={{ url: <span className='underline'>{suggestion.originalUrl.length > 20 ? suggestion.originalUrl.slice(0, 20) + '…' : suggestion.originalUrl}</span> }}
|
||||
/>
|
||||
</span>
|
||||
<HStack space={2} justifyContent='end'>
|
||||
<Button
|
||||
theme='muted'
|
||||
size='xs'
|
||||
onClick={() => handleReject(suggestion.key)}
|
||||
>
|
||||
<FormattedMessage id='compose.clear_link_suggestion.ignore' defaultMessage='Ignore' />
|
||||
</Button>
|
||||
<Button
|
||||
theme='muted'
|
||||
size='xs'
|
||||
onClick={() => handleAccept(suggestion.key)}
|
||||
>
|
||||
<FormattedMessage id='compose.clear_link_suggestion.remove' defaultMessage='Remove' />
|
||||
</Button>
|
||||
</HStack>
|
||||
</Stack>
|
||||
)}
|
||||
</OptionalMotion>
|
||||
);
|
||||
};
|
||||
|
||||
export { ClearLinkSuggestion as default };
|
||||
@ -1,5 +1,5 @@
|
||||
import clsx from 'clsx';
|
||||
import { CLEAR_EDITOR_COMMAND, TextNode, type LexicalEditor } from 'lexical';
|
||||
import { $getNodeByKey, CLEAR_EDITOR_COMMAND, TextNode, type LexicalEditor } from 'lexical';
|
||||
import React, { Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
@ -11,6 +11,8 @@ import {
|
||||
fetchComposeSuggestions,
|
||||
selectComposeSuggestion,
|
||||
uploadCompose,
|
||||
ignoreClearLinkSuggestion,
|
||||
suggestClearLink,
|
||||
} from 'pl-fe/actions/compose';
|
||||
import Button from 'pl-fe/components/ui/button';
|
||||
import HStack from 'pl-fe/components/ui/hstack';
|
||||
@ -30,6 +32,7 @@ import WarningContainer from '../containers/warning-container';
|
||||
import { $createEmojiNode } from '../editor/nodes/emoji-node';
|
||||
import { countableText } from '../util/counter';
|
||||
|
||||
import ClearLinkSuggestion from './clear-link-suggestion';
|
||||
import ContentTypeButton from './content-type-button';
|
||||
import InteractionPolicyButton from './interaction-policy-button';
|
||||
import LanguageDropdown from './language-dropdown';
|
||||
@ -47,6 +50,7 @@ import UploadForm from './upload-form';
|
||||
import VisualCharacterCounter from './visual-character-counter';
|
||||
import Warning from './warning';
|
||||
|
||||
import type { LinkNode } from '@lexical/link';
|
||||
import type { AutoSuggestion } from 'pl-fe/components/autosuggest-input';
|
||||
import type { Emoji } from 'pl-fe/features/emoji';
|
||||
|
||||
@ -172,6 +176,29 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
dispatch(uploadCompose(id, files, intl));
|
||||
};
|
||||
|
||||
const onAcceptClearLinkSuggestion = (key: string) => {
|
||||
const editor = editorRef.current;
|
||||
const suggestion = compose.clear_link_suggestion;
|
||||
if (!editor || !suggestion) return;
|
||||
|
||||
editor.update(() => {
|
||||
const node: LinkNode | null = $getNodeByKey(key);
|
||||
if (node) {
|
||||
node.setURL(suggestion.cleanUrl);
|
||||
const children = node.getChildren();
|
||||
const textNode = children[0] as TextNode;
|
||||
if (children.length === 1 && textNode.getType() === 'text' && textNode.getTextContent() === suggestion.originalUrl) {
|
||||
textNode.setTextContent(suggestion.cleanUrl);
|
||||
}
|
||||
}
|
||||
dispatch(suggestClearLink(id, null));
|
||||
});
|
||||
};
|
||||
|
||||
const onRejectClearLinkSuggestion = (key: string) => {
|
||||
dispatch(ignoreClearLinkSuggestion(id, key));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('click', handleClick, true);
|
||||
|
||||
@ -281,6 +308,8 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
<ClearLinkSuggestion composeId={id} handleAccept={onAcceptClearLinkSuggestion} handleReject={onRejectClearLinkSuggestion} />
|
||||
|
||||
{composeModifiers}
|
||||
|
||||
<QuotedStatusContainer composeId={id} />
|
||||
|
||||
Reference in New Issue
Block a user