Multilanguage posting
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
@@ -87,6 +87,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
schedule: scheduledAt,
|
||||
group_id: groupId,
|
||||
text,
|
||||
modified_language: modifiedLanguage,
|
||||
} = compose;
|
||||
|
||||
const prevSpoiler = usePrevious(spoiler);
|
||||
@@ -282,6 +283,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
<div>
|
||||
<Suspense>
|
||||
<ComposeEditor
|
||||
key={modifiedLanguage}
|
||||
ref={editorRef}
|
||||
className='mt-2'
|
||||
composeId={id}
|
||||
|
||||
@@ -7,7 +7,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { addComposeLanguage, changeComposeLanguage, deleteComposeLanguage } from 'soapbox/actions/compose';
|
||||
import { addComposeLanguage, changeComposeLanguage, changeComposeModifiedLanguage, deleteComposeLanguage } from 'soapbox/actions/compose';
|
||||
import { Button, Icon, Input, Portal } from 'soapbox/components/ui';
|
||||
import { type Language, languages as languagesObject } from 'soapbox/features/preferences';
|
||||
import { useAppDispatch, useAppSelector, useCompose, useFeatures } from 'soapbox/hooks';
|
||||
@@ -66,7 +66,12 @@ const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
|
||||
],
|
||||
});
|
||||
|
||||
const { language, suggested_language: suggestedLanguage, textMap } = useCompose(composeId);
|
||||
const {
|
||||
language,
|
||||
modified_language: modifiedLanguage,
|
||||
suggested_language: suggestedLanguage,
|
||||
textMap,
|
||||
} = useCompose(composeId);
|
||||
|
||||
const handleClick: React.EventHandler<
|
||||
React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLButtonElement>
|
||||
@@ -87,8 +92,6 @@ const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (language: Language | null) => dispatch(changeComposeLanguage(composeId, language));
|
||||
|
||||
const handleOptionKeyDown: React.KeyboardEventHandler = e => {
|
||||
const value = e.currentTarget.getAttribute('data-index');
|
||||
const index = results.findIndex(([key]) => key === value);
|
||||
@@ -125,10 +128,17 @@ const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
|
||||
const handleOptionClick: React.EventHandler<any> = (e: MouseEvent | KeyboardEvent) => {
|
||||
const value = (e.currentTarget as HTMLElement)?.getAttribute('data-index') as Language;
|
||||
|
||||
if (textMap.size) {
|
||||
if (!(textMap.has(value) || language === value)) return;
|
||||
|
||||
dispatch(changeComposeModifiedLanguage(composeId, value));
|
||||
} else {
|
||||
dispatch(changeComposeLanguage(composeId, value));
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
setIsOpen(false);
|
||||
handleChange(value);
|
||||
};
|
||||
|
||||
const handleAddLanguageClick: React.EventHandler<any> = (e: MouseEvent | KeyboardEvent) => {
|
||||
@@ -288,7 +298,7 @@ const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
|
||||
|
||||
let buttonLabel = intl.formatMessage(messages.languagePrompt);
|
||||
if (language) {
|
||||
const list: string[] = [languagesObject[language]];
|
||||
const list: string[] = [languagesObject[modifiedLanguage || language]];
|
||||
if (textMap.size) list.push(intl.formatMessage(messages.multipleLanguages, {
|
||||
count: textMap.size,
|
||||
}));
|
||||
@@ -347,6 +357,7 @@ const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
|
||||
<div className='h-96 w-full overflow-scroll' ref={node} tabIndex={-1}>
|
||||
{results.map(([code, name]) => {
|
||||
const active = code === language;
|
||||
const modified = code === modifiedLanguage;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -357,15 +368,20 @@ const LanguageDropdown: React.FC<ILanguageDropdown> = ({ composeId }) => {
|
||||
onKeyDown={handleOptionKeyDown}
|
||||
onClick={handleOptionClick}
|
||||
className={clsx(
|
||||
'flex cursor-pointer gap-2 p-2.5 text-sm text-gray-700 hover:bg-gray-100 black:hover:bg-gray-900 dark:text-gray-400 dark:hover:bg-gray-800',
|
||||
{ 'bg-gray-100 dark:bg-gray-800 black:bg-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700': active },
|
||||
'flex gap-2 p-2.5 text-sm text-gray-700 dark:text-gray-400',
|
||||
{
|
||||
'bg-gray-100 dark:bg-gray-800 black:bg-gray-900 cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-700': modified,
|
||||
'cursor-pointer hover:bg-gray-100 black:hover:bg-gray-900 dark:hover:bg-gray-800': !textMap.size || textMap.has(code),
|
||||
'cursor-pointer': active,
|
||||
'cursor-default': !active && !(!textMap.size || textMap.has(code)),
|
||||
},
|
||||
)}
|
||||
aria-selected={active}
|
||||
ref={active ? focusedItem : null}
|
||||
>
|
||||
<div
|
||||
className={clsx('flex-auto grow text-primary-600 dark:text-primary-400', {
|
||||
'text-black dark:text-white': active,
|
||||
'text-black dark:text-white': modified,
|
||||
})}
|
||||
>
|
||||
{name}
|
||||
|
||||
@@ -48,7 +48,7 @@ const Option: React.FC<IOption> = ({
|
||||
const dispatch = useAppDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const suggestions = useCompose(composeId).suggestions;
|
||||
const { suggestions } = useCompose(composeId);
|
||||
|
||||
const handleOptionTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => onChange(index, event.target.value);
|
||||
|
||||
@@ -112,11 +112,11 @@ const PollForm: React.FC<IPollForm> = ({ composeId }) => {
|
||||
const intl = useIntl();
|
||||
const { configuration } = useInstance();
|
||||
|
||||
const compose = useCompose(composeId);
|
||||
const { poll, language, modified_language: modifiedLanguage } = useCompose(composeId);
|
||||
|
||||
const options = compose.poll?.options;
|
||||
const expiresIn = compose.poll?.expires_in;
|
||||
const isMultiple = compose.poll?.multiple;
|
||||
const options = !modifiedLanguage || modifiedLanguage === language ? poll?.options : poll?.options_map.map((option, key) => option.get(modifiedLanguage, poll.options.get(key)!));
|
||||
const expiresIn = poll?.expires_in;
|
||||
const isMultiple = poll?.multiple;
|
||||
|
||||
const {
|
||||
max_options: maxOptions,
|
||||
|
||||
@@ -26,7 +26,7 @@ const SpoilerInput = React.forwardRef<AutosuggestInput, ISpoilerInput>(({
|
||||
}, ref) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
const compose = useCompose(composeId);
|
||||
const { language, modified_language, spoiler, spoiler_text: spoilerText, spoilerTextMap, suggestions } = useCompose(composeId);
|
||||
|
||||
const handleChangeSpoilerText: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
dispatch(changeComposeSpoilerText(composeId, e.target.value));
|
||||
@@ -36,12 +36,14 @@ const SpoilerInput = React.forwardRef<AutosuggestInput, ISpoilerInput>(({
|
||||
dispatch(changeComposeSpoilerness(composeId));
|
||||
};
|
||||
|
||||
const value = !modified_language || modified_language === language ? spoilerText : spoilerTextMap.get(modified_language, '');
|
||||
|
||||
return (
|
||||
<Stack
|
||||
space={4}
|
||||
className={clsx({
|
||||
'relative transition-height': true,
|
||||
'hidden': !compose.spoiler,
|
||||
'hidden': !spoiler,
|
||||
})}
|
||||
>
|
||||
<Divider />
|
||||
@@ -53,10 +55,10 @@ const SpoilerInput = React.forwardRef<AutosuggestInput, ISpoilerInput>(({
|
||||
|
||||
<AutosuggestInput
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
value={compose.spoiler_text}
|
||||
value={value}
|
||||
onChange={handleChangeSpoilerText}
|
||||
disabled={!compose.spoiler}
|
||||
suggestions={compose.suggestions}
|
||||
disabled={!spoiler}
|
||||
suggestions={suggestions}
|
||||
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
||||
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
||||
onSuggestionSelected={onSuggestionSelected}
|
||||
|
||||
@@ -90,7 +90,8 @@ const ComposeEditor = React.forwardRef<LexicalEditor, IComposeEditor>(({
|
||||
placeholder,
|
||||
}, ref) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const isWysiwyg = useCompose(composeId).content_type === 'wysiwyg';
|
||||
const { content_type: contentType } = useCompose(composeId);
|
||||
const isWysiwyg = contentType === 'wysiwyg';
|
||||
const nodes = useNodes(isWysiwyg);
|
||||
|
||||
const [suggestionsHidden, setSuggestionsHidden] = useState(true);
|
||||
@@ -106,20 +107,28 @@ const ComposeEditor = React.forwardRef<LexicalEditor, IComposeEditor>(({
|
||||
|
||||
if (!compose) return;
|
||||
|
||||
if (compose.editorState) {
|
||||
return compose.editorState;
|
||||
const editorState = !compose.modified_language || compose.modified_language === compose.language
|
||||
? compose.editorState
|
||||
: compose.editorStateMap.get(compose.modified_language, '');
|
||||
|
||||
if (editorState) {
|
||||
return editorState;
|
||||
}
|
||||
|
||||
return () => {
|
||||
const text = !compose.modified_language || compose.modified_language === compose.language
|
||||
? compose.text
|
||||
: compose.textMap.get(compose.modified_language, '');
|
||||
|
||||
if (isWysiwyg) {
|
||||
$createRemarkImport({
|
||||
handlers: {
|
||||
image: importImage,
|
||||
},
|
||||
})(compose.text);
|
||||
})(text);
|
||||
} else {
|
||||
const paragraph = $createParagraphNode();
|
||||
const textNode = $createTextNode(compose.text);
|
||||
const textNode = $createTextNode(text);
|
||||
|
||||
paragraph.append(textNode);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user