From 4cbe9c111235b0949050a6111461880607d37a83 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Thu, 30 Mar 2023 18:25:01 -0500 Subject: [PATCH] Composer: Respect `no_webpage` option when editing a message (#2804) --- .../middle/composer/hooks/useEditing.ts | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/components/middle/composer/hooks/useEditing.ts b/src/components/middle/composer/hooks/useEditing.ts index e86e2c3ee..3b4cbd291 100644 --- a/src/components/middle/composer/hooks/useEditing.ts +++ b/src/components/middle/composer/hooks/useEditing.ts @@ -4,9 +4,10 @@ import { getActions } from '../../../../global'; import type { ApiFormattedText, ApiMessage } from '../../../../api/types'; import type { MessageListType } from '../../../../global/types'; import type { Signal } from '../../../../util/signals'; +import { ApiMessageEntityTypes } from '../../../../api/types'; -import useEffectWithPrevDeps from '../../../../hooks/useEffectWithPrevDeps'; import { EDITABLE_INPUT_CSS_SELECTOR } from '../../../../config'; +import useEffectWithPrevDeps from '../../../../hooks/useEffectWithPrevDeps'; import parseMessageInput from '../../../../util/parseMessageInput'; import focusEditableElement from '../../../../util/focusEditableElement'; import { hasMessageMedia } from '../../../../global/helpers'; @@ -14,6 +15,11 @@ import { getTextWithEntitiesAsHtml } from '../../../common/helpers/renderTextWit import { fastRaf } from '../../../../util/schedulers'; import useBackgroundMode from '../../../../hooks/useBackgroundMode'; import useBeforeUnload from '../../../../hooks/useBeforeUnload'; +import { useDebouncedResolver } from '../../../../hooks/useAsyncResolvers'; +import useDerivedSignal from '../../../../hooks/useDerivedSignal'; + +const URL_ENTITIES = new Set([ApiMessageEntityTypes.TextUrl, ApiMessageEntityTypes.Url]); +const DEBOUNCE_MS = 300; const useEditing = ( getHtml: Signal, @@ -28,7 +34,7 @@ const useEditing = ( editingDraft?: ApiFormattedText, replyingToId?: number, ): [VoidFunction, VoidFunction, boolean] => { - const { editMessage, setEditingDraft } = getActions(); + const { editMessage, setEditingDraft, toggleMessageWebPage } = getActions(); const [shouldForceShowEditing, setShouldForceShowEditing] = useState(); useEffectWithPrevDeps(([prevEditedMessage, prevReplyingToId]) => { @@ -61,6 +67,21 @@ const useEditing = ( // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `as const` not yet supported by linter }, [editedMessage, replyingToId, setHtml] as const); + useEffect(() => { + if (!editedMessage) { + return; + } + + const shouldSetNoWebPage = !('webPage' in editedMessage.content) + && editedMessage.content.text?.entities?.some((entity) => URL_ENTITIES.has(entity.type)); + + toggleMessageWebPage({ + chatId, + threadId, + noWebPage: shouldSetNoWebPage, + }); + }, [chatId, threadId, editedMessage]); + useEffect(() => { if (!editedMessage) return undefined; return () => { @@ -73,6 +94,32 @@ const useEditing = ( }; }, [chatId, editedMessage, getHtml, setEditingDraft, threadId, type]); + const detectLinkDebounced = useDebouncedResolver(() => { + if (!editedMessage) return false; + + const edited = parseMessageInput(getHtml()); + return !('webPage' in editedMessage.content) + && editedMessage.content.text?.entities?.some((entity) => URL_ENTITIES.has(entity.type)) + && !(edited.entities?.some((entity) => URL_ENTITIES.has(entity.type))); + }, [editedMessage, getHtml], DEBOUNCE_MS, true); + + const getShouldResetNoWebPageDebounced = useDerivedSignal(detectLinkDebounced, [detectLinkDebounced, getHtml], true); + + useEffectWithPrevDeps(([prevEditedMessage]) => { + if (!editedMessage || prevEditedMessage?.id !== editedMessage.id) { + return; + } + + if (getShouldResetNoWebPageDebounced()) { + toggleMessageWebPage({ + chatId, + threadId, + noWebPage: false, + }); + } + // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `as const` not yet supported by linter + }, [editedMessage, chatId, getHtml, threadId] as const); + const restoreNewDraftAfterEditing = useCallback(() => { if (!draft) return; // Run 1 frame after editing draft reset