Files
ncd-fe/packages/pl-fe/src/features/compose/editor/plugins/state-plugin.tsx
marcin mikołajczak 563e5288fb pl-fe: Remove some immutable usage
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
2024-11-05 23:32:43 +01:00

109 lines
3.5 KiB
TypeScript

import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import { $createRemarkExport } from '@mkljczk/lexical-remark';
import { $getRoot } from 'lexical';
import debounce from 'lodash/debounce';
import { useCallback, useEffect } from 'react';
import { useIntl } from 'react-intl';
import { addSuggestedLanguage, addSuggestedQuote, setEditorState } from 'pl-fe/actions/compose';
import { fetchStatus } from 'pl-fe/actions/statuses';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useFeatures } from 'pl-fe/hooks/use-features';
import { getStatusIdsFromLinksInContent } from 'pl-fe/utils/status';
import type { LanguageIdentificationModel } from 'fasttext.wasm.js/dist/models/language-identification/common.js';
let lidModel: LanguageIdentificationModel;
interface IStatePlugin {
composeId: string;
isWysiwyg?: boolean;
}
const StatePlugin: React.FC<IStatePlugin> = ({ composeId, isWysiwyg }) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const [editor] = useLexicalComposerContext();
const features = useFeatures();
const getQuoteSuggestions = useCallback(debounce((text: string) => {
dispatch(async (_, getState) => {
const state = getState();
const compose = state.compose.get(composeId);
if (!features.quotePosts || compose?.quote) return;
const ids = getStatusIdsFromLinksInContent(text);
let quoteId: string | undefined;
for (const id of ids) {
if (compose?.dismissed_quotes.includes(id)) continue;
if (state.statuses[id]) {
quoteId = id;
break;
}
const status = await dispatch(fetchStatus(id, intl));
if (status) {
quoteId = status.id;
break;
}
}
if (quoteId) dispatch(addSuggestedQuote(composeId, quoteId));
});
}, 2000), []);
const detectLanguage = useCallback(debounce(async (text: string) => {
dispatch(async (dispatch, getState) => {
const state = getState();
const compose = state.compose.get(composeId);
if (!features.postLanguages || features.languageDetection || compose?.language) return;
const wordsLength = text.split(/\s+/).length;
if (wordsLength < 4) return;
if (!lidModel) {
// eslint-disable-next-line import/extensions
const { getLIDModel } = await import('fasttext.wasm.js/common');
lidModel = await getLIDModel();
}
if (!lidModel.model) await lidModel.load();
const { alpha2, possibility } = await lidModel.identify(text.replace(/\s+/i, ' '));
if (alpha2 && possibility > 0.5) {
dispatch(addSuggestedLanguage(composeId, alpha2));
}
});
}, 750), []);
useEffect(() => {
return editor.registerUpdateListener(({ editorState }) => {
const plainText = editorState.read(() => $getRoot().getTextContent());
let text = plainText;
if (isWysiwyg) {
text = editorState.read($createRemarkExport({
handlers: {
hashtag: (node) => ({ type: 'text', value: node.getTextContent() }),
mention: (node) => ({ type: 'text', value: node.getTextContent() }),
},
}));
}
const isEmpty = text === '';
const data = isEmpty ? null : JSON.stringify(editorState.toJSON());
dispatch(setEditorState(composeId, data, text));
getQuoteSuggestions(plainText);
detectLanguage(plainText);
});
}, [editor]);
return null;
};
export { StatePlugin as default };