diff --git a/src/components/compose.jsx b/src/components/compose.jsx index 07ac456..890c435 100644 --- a/src/components/compose.jsx +++ b/src/components/compose.jsx @@ -1,14 +1,17 @@ import './compose.css'; import '@github/text-expander-element'; +import { forwardRef } from 'preact/compat'; import { useEffect, useMemo, useRef, useState } from 'preact/hooks'; import { useHotkeys } from 'react-hotkeys-hook'; import stringLength from 'string-length'; +import { useSnapshot } from 'valtio'; import supportedLanguages from '../data/status-supported-languages'; import urlRegex from '../data/url-regex'; import emojifyText from '../utils/emojify-text'; import openCompose from '../utils/open-compose'; +import states from '../utils/states'; import store from '../utils/store'; import visibilityIconsMap from '../utils/visibility-icons-map'; @@ -54,6 +57,16 @@ menu.className = 'text-expander-menu'; const DEFAULT_LANG = 'en'; +// https://github.com/mastodon/mastodon/blob/c4a429ed47e85a6bbf0d470a41cc2f64cf120c19/app/javascript/mastodon/features/compose/util/counter.js +const urlRegexObj = new RegExp(urlRegex.source, urlRegex.flags); +const usernameRegex = /(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/gi; +const urlPlaceholder = '$2xxxxxxxxxxxxxxxxxxxxxxx'; +function countableText(inputText) { + return inputText + .replace(urlRegexObj, urlPlaceholder) + .replace(usernameRegex, '$1@$3'); +} + function Compose({ onClose, replyToStatus, @@ -62,6 +75,7 @@ function Compose({ standalone, hasOpener, }) { + console.warn('RENDER COMPOSER'); const [uiState, setUIState] = useState('default'); const accounts = store.local.getJSON('accounts'); @@ -223,130 +237,6 @@ function Compose({ } }, [draftStatus, editStatus, replyToStatus]); - const textExpanderRef = useRef(); - const textExpanderTextRef = useRef(''); - useEffect(() => { - if (textExpanderRef.current) { - const handleChange = (e) => { - // console.log('text-expander-change', e); - const { key, provide, text } = e.detail; - textExpanderTextRef.current = text; - - if (text === '') { - provide( - Promise.resolve({ - matched: false, - }), - ); - return; - } - - if (key === ':') { - // const emojis = customEmojis.current.filter((emoji) => - // emoji.shortcode.startsWith(text), - // ); - const emojis = filterShortcodes(customEmojis.current, text); - let html = ''; - emojis.forEach((emoji) => { - const { shortcode, url } = emoji; - html += ` -
  • - - :${encodeHTML(shortcode)}: -
  • `; - }); - // console.log({ emojis, html }); - menu.innerHTML = html; - provide( - Promise.resolve({ - matched: emojis.length > 0, - fragment: menu, - }), - ); - return; - } - - const type = { - '@': 'accounts', - '#': 'hashtags', - }[key]; - provide( - new Promise((resolve) => { - const searchResults = masto.v2.search({ - type, - q: text, - limit: 5, - }); - searchResults.then((value) => { - if (text !== textExpanderTextRef.current) { - return; - } - console.log({ value, type, v: value[type] }); - const results = value[type]; - console.log('RESULTS', value, results); - let html = ''; - results.forEach((result) => { - const { - name, - avatarStatic, - displayName, - username, - acct, - emojis, - } = result; - const displayNameWithEmoji = emojifyText(displayName, emojis); - // const item = menuItem.cloneNode(); - if (acct) { - html += ` -
  • - - - - - ${displayNameWithEmoji || username} -
    @${encodeHTML(acct)} -
    -
  • - `; - } else { - html += ` -
  • - #${encodeHTML(name)} -
  • - `; - } - menu.innerHTML = html; - }); - console.log('MENU', results, menu); - resolve({ - matched: results.length > 0, - fragment: menu, - }); - }); - }), - ); - }; - - textExpanderRef.current.addEventListener( - 'text-expander-change', - handleChange, - ); - - textExpanderRef.current.addEventListener('text-expander-value', (e) => { - const { key, item } = e.detail; - if (key === ':') { - e.detail.value = `:${item.dataset.value}:`; - } else { - e.detail.value = `${key}${item.dataset.value}`; - } - }); - } - }, []); - const formRef = useRef(); const beforeUnloadCopy = @@ -432,19 +322,16 @@ function Compose({ }); }, []); - const [charCount, setCharCount] = useState( - textareaRef.current?.value?.length + - spoilerTextRef.current?.value?.length || 0, - ); - const leftChars = maxCharacters - charCount; const getCharCount = () => { const { value } = textareaRef.current; const { value: spoilerText } = spoilerTextRef.current; return stringLength(countableText(value)) + stringLength(spoilerText); }; const updateCharCount = () => { - setCharCount(getCharCount()); + const count = getCharCount(); + states.composerCharacterCount = count; }; + useEffect(updateCharCount, []); useHotkeys( 'esc', @@ -818,41 +705,22 @@ function Compose({ {' '} - - - +