pl-fe: remove immutable usage from compose reducer

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-11-14 15:21:01 +01:00
parent e53be89a85
commit e8c89a4415
8 changed files with 43 additions and 46 deletions

View file

@ -318,7 +318,7 @@ const needsDescriptions = (state: RootState, composeId: string) => {
const media = state.compose[composeId]!.media_attachments; const media = state.compose[composeId]!.media_attachments;
const missingDescriptionModal = useSettingsStore.getState().settings.missingDescriptionModal; const missingDescriptionModal = useSettingsStore.getState().settings.missingDescriptionModal;
const hasMissing = media.filter(item => !item.description).size > 0; const hasMissing = media.filter(item => !item.description).length > 0;
return missingDescriptionModal && hasMissing; return missingDescriptionModal && hasMissing;
}; };
@ -357,7 +357,7 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
return; return;
} }
if ((!status || !status.length) && media.size === 0) { if ((!status || !status.length) && media.length === 0) {
return; return;
} }
@ -392,7 +392,7 @@ const submitCompose = (composeId: string, opts: SubmitComposeOpts = {}) =>
status, status,
in_reply_to_id: compose.in_reply_to || undefined, in_reply_to_id: compose.in_reply_to || undefined,
quote_id: compose.quote || undefined, quote_id: compose.quote || undefined,
media_ids: media.map(item => item.id).toArray(), media_ids: media.map(item => item.id),
sensitive: compose.sensitive, sensitive: compose.sensitive,
spoiler_text: compose.spoiler_text, spoiler_text: compose.spoiler_text,
visibility: compose.privacy, visibility: compose.privacy,
@ -471,7 +471,7 @@ const uploadCompose = (composeId: string, files: FileList, intl: IntlShape) =>
const progress = new Array(files.length).fill(0); const progress = new Array(files.length).fill(0);
let total = Array.from(files).reduce((a, v) => a + v.size, 0); let total = Array.from(files).reduce((a, v) => a + v.size, 0);
const mediaCount = media ? media.size : 0; const mediaCount = media ? media.length : 0;
if (files.length + mediaCount > attachmentLimit) { if (files.length + mediaCount > attachmentLimit) {
toast.error(messages.uploadErrorLimit); toast.error(messages.uploadErrorLimit);
@ -726,7 +726,7 @@ const insertIntoTagHistory = (composeId: string, recognizedTags: Array<Tag>, tex
.map(tag => tag.name); .map(tag => tag.name);
const intersectedOldHistory = oldHistory.filter(name => names.findIndex(newName => newName.toLowerCase() === name.toLowerCase()) === -1); const intersectedOldHistory = oldHistory.filter(name => names.findIndex(newName => newName.toLowerCase() === name.toLowerCase()) === -1);
names.push(...intersectedOldHistory.toJS()); names.push(...intersectedOldHistory);
const newHistory = names.slice(0, 1000); const newHistory = names.slice(0, 1000);

View file

@ -22,7 +22,7 @@ const checkComposeContent = (compose?: Compose) =>
!!compose && [ !!compose && [
compose.editorState && compose.editorState.length > 0, compose.editorState && compose.editorState.length > 0,
compose.spoiler_text.length > 0, compose.spoiler_text.length > 0,
compose.media_attachments.size > 0, compose.media_attachments.length > 0,
compose.poll !== null, compose.poll !== null,
].some(check => check === true); ].some(check => check === true);

View file

@ -96,7 +96,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
const hasPoll = !!compose.poll; const hasPoll = !!compose.poll;
const isEditing = compose.id !== null; const isEditing = compose.id !== null;
const anyMedia = compose.media_attachments.size > 0; const anyMedia = compose.media_attachments.length > 0;
const [composeFocused, setComposeFocused] = useState(false); const [composeFocused, setComposeFocused] = useState(false);
@ -189,7 +189,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
</HStack> </HStack>
), [features, id, anyMedia]); ), [features, id, anyMedia]);
const showModifiers = !condensed && (compose.media_attachments.size || compose.is_uploading || compose.poll?.options.length || compose.schedule); const showModifiers = !condensed && (compose.media_attachments.length || compose.is_uploading || compose.poll?.options.length || compose.schedule);
const composeModifiers = showModifiers && ( const composeModifiers = showModifiers && (
<Stack space={4} className='font-[inherit] text-sm text-gray-900'> <Stack space={4} className='font-[inherit] text-sm text-gray-900'>

View file

@ -90,7 +90,7 @@ const Option: React.FC<IOption> = ({
maxLength={maxChars} maxLength={maxChars}
value={title} value={title}
onChange={handleOptionTitleChange} onChange={handleOptionTitleChange}
suggestions={suggestions.toArray()} suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested} onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested} onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={onSuggestionSelected} onSuggestionSelected={onSuggestionSelected}

View file

@ -37,7 +37,7 @@ const SpoilerInput: React.FC<ISpoilerInput> = ({
placeholder={intl.formatMessage(messages.placeholder)} placeholder={intl.formatMessage(messages.placeholder)}
value={value} value={value}
onChange={handleChangeSpoilerText} onChange={handleChangeSpoilerText}
suggestions={suggestions.toArray()} suggestions={suggestions}
onSuggestionsFetchRequested={onSuggestionsFetchRequested} onSuggestionsFetchRequested={onSuggestionsFetchRequested}
onSuggestionsClearRequested={onSuggestionsClearRequested} onSuggestionsClearRequested={onSuggestionsClearRequested}
onSuggestionSelected={onSuggestionSelected} onSuggestionSelected={onSuggestionSelected}

View file

@ -38,13 +38,13 @@ const UploadForm: React.FC<IUploadForm> = ({ composeId, onSubmit }) => {
dragOverItem.current = null; dragOverItem.current = null;
}, [dragItem, dragOverItem]); }, [dragItem, dragOverItem]);
if (!isUploading && mediaIds.isEmpty()) return null; if (!isUploading && !mediaIds.length) return null;
return ( return (
<div className='overflow-hidden'> <div className='overflow-hidden'>
<UploadProgress composeId={composeId} /> <UploadProgress composeId={composeId} />
<HStack wrap className={clsx('overflow-hidden', mediaIds.size !== 0 && 'm-[-5px]')}> <HStack wrap className={clsx('overflow-hidden', mediaIds.length > 0 && 'm-[-5px]')}>
{mediaIds.map((id: string) => ( {mediaIds.map((id: string) => (
<Upload <Upload
id={id} id={id}

View file

@ -303,7 +303,7 @@ const AutosuggestPlugin = ({
}; };
const onSelectSuggestion = (index: number) => { const onSelectSuggestion = (index: number) => {
const suggestion = suggestions.get(index) as AutoSuggestion; const suggestion = suggestions[index];
editor.update(() => { editor.update(() => {
dispatch((dispatch, getState) => { dispatch((dispatch, getState) => {
@ -446,11 +446,11 @@ const AutosuggestPlugin = ({
]); ]);
useEffect(() => { useEffect(() => {
if (suggestions && suggestions.size > 0) setSuggestionsHidden(false); if (suggestions && suggestions.length > 0) setSuggestionsHidden(false);
}, [suggestions]); }, [suggestions]);
useEffect(() => { useEffect(() => {
if (resolution !== null && !suggestionsHidden && !suggestions.isEmpty()) { if (resolution !== null && !suggestionsHidden && suggestions.length) {
const handleClick = (event: MouseEvent) => { const handleClick = (event: MouseEvent) => {
const target = event.target as HTMLElement; const target = event.target as HTMLElement;
@ -462,7 +462,7 @@ const AutosuggestPlugin = ({
return () => document.removeEventListener('click', handleClick); return () => document.removeEventListener('click', handleClick);
} }
}, [resolution, suggestionsHidden, suggestions.isEmpty()]); }, [resolution, suggestionsHidden, !suggestions.length]);
useEffect(() => { useEffect(() => {
if (resolution === null) return; if (resolution === null) return;
@ -472,8 +472,8 @@ const AutosuggestPlugin = ({
KEY_ARROW_UP_COMMAND, KEY_ARROW_UP_COMMAND,
(payload) => { (payload) => {
const event = payload; const event = payload;
if (suggestions !== null && suggestions.size && selectedSuggestion !== null) { if (suggestions !== null && suggestions.length && selectedSuggestion !== null) {
const newSelectedSuggestion = selectedSuggestion !== 0 ? selectedSuggestion - 1 : suggestions.size - 1; const newSelectedSuggestion = selectedSuggestion !== 0 ? selectedSuggestion - 1 : suggestions.length - 1;
setSelectedSuggestion(newSelectedSuggestion); setSelectedSuggestion(newSelectedSuggestion);
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
@ -486,8 +486,8 @@ const AutosuggestPlugin = ({
KEY_ARROW_DOWN_COMMAND, KEY_ARROW_DOWN_COMMAND,
(payload) => { (payload) => {
const event = payload; const event = payload;
if (suggestions !== null && suggestions.size && selectedSuggestion !== null) { if (suggestions !== null && suggestions.length && selectedSuggestion !== null) {
const newSelectedSuggestion = selectedSuggestion !== suggestions.size - 1 ? selectedSuggestion + 1 : 0; const newSelectedSuggestion = selectedSuggestion !== suggestions.length - 1 ? selectedSuggestion + 1 : 0;
setSelectedSuggestion(newSelectedSuggestion); setSelectedSuggestion(newSelectedSuggestion);
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
@ -543,8 +543,8 @@ const AutosuggestPlugin = ({
<div <div
className={clsx({ className={clsx({
'scroll-smooth snap-y snap-always will-change-scroll mt-6 overflow-y-auto max-h-56 relative w-max z-[1000] shadow bg-white dark:bg-gray-900 rounded-lg py-1 space-y-0 dark:ring-2 dark:ring-primary-700 focus:outline-none': true, 'scroll-smooth snap-y snap-always will-change-scroll mt-6 overflow-y-auto max-h-56 relative w-max z-[1000] shadow bg-white dark:bg-gray-900 rounded-lg py-1 space-y-0 dark:ring-2 dark:ring-primary-700 focus:outline-none': true,
hidden: suggestionsHidden || suggestions.isEmpty(), hidden: suggestionsHidden || !suggestions.length,
block: !suggestionsHidden && !suggestions.isEmpty(), block: !suggestionsHidden && suggestions.length,
})} })}
> >
{suggestions.map(renderSuggestion)} {suggestions.map(renderSuggestion)}

View file

@ -1,9 +1,8 @@
import { List as ImmutableList } from 'immutable';
import { create } from 'mutative'; import { create } from 'mutative';
import { Instance, PLEROMA, type CredentialAccount, type MediaAttachment, type Tag } from 'pl-api'; import { PLEROMA, type CredentialAccount, type Instance, type MediaAttachment, type Tag } from 'pl-api';
import { INSTANCE_FETCH_SUCCESS, InstanceAction } from 'pl-fe/actions/instance'; import { INSTANCE_FETCH_SUCCESS, InstanceAction } from 'pl-fe/actions/instance';
import { isNativeEmoji } from 'pl-fe/features/emoji'; import { isNativeEmoji, type Emoji } from 'pl-fe/features/emoji';
import { tagHistory } from 'pl-fe/settings'; import { tagHistory } from 'pl-fe/settings';
import { hasIntegerMediaIds } from 'pl-fe/utils/status'; import { hasIntegerMediaIds } from 'pl-fe/utils/status';
@ -69,11 +68,9 @@ import { FE_NAME } from '../actions/settings';
import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines'; import { TIMELINE_DELETE, type TimelineAction } from '../actions/timelines';
import { unescapeHTML } from '../utils/html'; import { unescapeHTML } from '../utils/html';
import type { Emoji } from 'pl-fe/features/emoji';
import type { Language } from 'pl-fe/features/preferences'; import type { Language } from 'pl-fe/features/preferences';
import type { Account } from 'pl-fe/normalizers/account'; import type { Account } from 'pl-fe/normalizers/account';
import type { Status } from 'pl-fe/normalizers/status'; import type { Status } from 'pl-fe/normalizers/status';
import type { APIEntity } from 'pl-fe/types/entities';
const getResetFileKey = () => Math.floor((Math.random() * 0x10000)); const getResetFileKey = () => Math.floor((Math.random() * 0x10000));
@ -109,7 +106,7 @@ interface Compose {
is_composing: boolean; is_composing: boolean;
is_submitting: boolean; is_submitting: boolean;
is_uploading: boolean; is_uploading: boolean;
media_attachments: ImmutableList<MediaAttachment>; media_attachments: Array<MediaAttachment>;
poll: ComposePoll | null; poll: ComposePoll | null;
privacy: string; privacy: string;
progress: number; progress: number;
@ -119,9 +116,9 @@ interface Compose {
sensitive: boolean; sensitive: boolean;
spoiler_text: string; spoiler_text: string;
spoilerTextMap: Partial<Record<Language, string>>; spoilerTextMap: Partial<Record<Language, string>>;
suggestions: ImmutableList<string>; suggestions: Array<string> | Array<Emoji>;
suggestion_token: string | null; suggestion_token: string | null;
tagHistory: ImmutableList<string>; tagHistory: Array<string>;
text: string; text: string;
textMap: Partial<Record<Language, string>>; textMap: Partial<Record<Language, string>>;
to: Array<string>; to: Array<string>;
@ -149,7 +146,7 @@ const newCompose = (params: Partial<Compose> = {}): Compose => ({
is_composing: false, is_composing: false,
is_submitting: false, is_submitting: false,
is_uploading: false, is_uploading: false,
media_attachments: ImmutableList<MediaAttachment>(), media_attachments: [],
poll: null, poll: null,
privacy: 'public', privacy: 'public',
progress: 0, progress: 0,
@ -159,9 +156,9 @@ const newCompose = (params: Partial<Compose> = {}): Compose => ({
sensitive: false, sensitive: false,
spoiler_text: '', spoiler_text: '',
spoilerTextMap: {}, spoilerTextMap: {},
suggestions: ImmutableList<string>(), suggestions: [],
suggestion_token: null, suggestion_token: null,
tagHistory: ImmutableList<string>(), tagHistory: [],
text: '', text: '',
textMap: {}, textMap: {},
to: [], to: [],
@ -204,7 +201,7 @@ const statusToMentionsAccountIdsArray = (status: Pick<Status, 'mentions' | 'acco
}; };
const appendMedia = (compose: Compose, media: MediaAttachment, defaultSensitive?: boolean) => { const appendMedia = (compose: Compose, media: MediaAttachment, defaultSensitive?: boolean) => {
const prevSize = compose.media_attachments.size; const prevSize = compose.media_attachments.length;
compose.media_attachments.push(media); compose.media_attachments.push(media);
compose.is_uploading = false; compose.is_uploading = false;
@ -217,7 +214,7 @@ const appendMedia = (compose: Compose, media: MediaAttachment, defaultSensitive?
}; };
const removeMedia = (compose: Compose, mediaId: string) => { const removeMedia = (compose: Compose, mediaId: string) => {
const prevSize = compose.media_attachments.size; const prevSize = compose.media_attachments.length;
compose.media_attachments = compose.media_attachments.filter(item => item.id !== mediaId); compose.media_attachments = compose.media_attachments.filter(item => item.id !== mediaId);
compose.idempotencyKey = crypto.randomUUID(); compose.idempotencyKey = crypto.randomUUID();
@ -235,17 +232,17 @@ const insertSuggestion = (compose: Compose, position: number, token: string | nu
compose.poll.options[path[2]] = updateText(compose.poll.options[path[2]]); compose.poll.options[path[2]] = updateText(compose.poll.options[path[2]]);
} }
compose.suggestion_token = null; compose.suggestion_token = null;
compose.suggestions = ImmutableList(); compose.suggestions = [];
compose.idempotencyKey = crypto.randomUUID(); compose.idempotencyKey = crypto.randomUUID();
}; };
const updateSuggestionTags = (compose: Compose, token: string, tags: Tag[]) => { const updateSuggestionTags = (compose: Compose, token: string, tags: Tag[]) => {
const prefix = token.slice(1); const prefix = token.slice(1);
compose.suggestions = ImmutableList(tags compose.suggestions = tags
.filter((tag) => tag.name.toLowerCase().startsWith(prefix.toLowerCase())) .filter((tag) => tag.name.toLowerCase().startsWith(prefix.toLowerCase()))
.slice(0, 4) .slice(0, 4)
.map((tag) => '#' + tag.name)); .map((tag) => '#' + tag.name);
compose.suggestion_token = token; compose.suggestion_token = token;
}; };
@ -299,7 +296,7 @@ const importAccount = (compose: Compose, account: CredentialAccount) => {
if (settings.defaultPrivacy) compose.privacy = settings.defaultPrivacy; if (settings.defaultPrivacy) compose.privacy = settings.defaultPrivacy;
if (settings.defaultContentType) compose.content_type = settings.defaultContentType; if (settings.defaultContentType) compose.content_type = settings.defaultContentType;
compose.tagHistory = ImmutableList(tagHistory.get(account.id)); compose.tagHistory = tagHistory.get(account.id);
}; };
// const updateSetting = (compose: Compose, path: string[], value: string) => { // const updateSetting = (compose: Compose, path: string[], value: string) => {
@ -493,12 +490,12 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | In
}); });
case COMPOSE_SUGGESTIONS_CLEAR: case COMPOSE_SUGGESTIONS_CLEAR:
return updateCompose(state, action.composeId, compose => { return updateCompose(state, action.composeId, compose => {
compose.suggestions = compose.suggestions.clear(); compose.suggestions = [];
compose.suggestion_token = null; compose.suggestion_token = null;
}); });
case COMPOSE_SUGGESTIONS_READY: case COMPOSE_SUGGESTIONS_READY:
return updateCompose(state, action.composeId, compose => { return updateCompose(state, action.composeId, compose => {
compose.suggestions = ImmutableList(action.accounts ? action.accounts.map((item: APIEntity) => item.id) : action.emojis); compose.suggestions = action.accounts ? action.accounts.map((item) => item.id) : action.emojis || [];
compose.suggestion_token = action.token; compose.suggestion_token = action.token;
}); });
case COMPOSE_SUGGESTION_SELECT: case COMPOSE_SUGGESTION_SELECT:
@ -507,7 +504,7 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | In
return updateCompose(state, action.composeId, compose => updateSuggestionTags(compose, action.token, action.tags)); return updateCompose(state, action.composeId, compose => updateSuggestionTags(compose, action.token, action.tags));
case COMPOSE_TAG_HISTORY_UPDATE: case COMPOSE_TAG_HISTORY_UPDATE:
return updateCompose(state, action.composeId, compose => { return updateCompose(state, action.composeId, compose => {
compose.tagHistory = ImmutableList(action.tags) as ImmutableList<string>; compose.tagHistory = action.tags;
}); });
case TIMELINE_DELETE: case TIMELINE_DELETE:
return updateCompose(state, 'compose-modal', compose => { return updateCompose(state, 'compose-modal', compose => {
@ -550,9 +547,9 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | In
compose.group_id = action.status.group_id; compose.group_id = action.status.group_id;
if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status)) { if (action.v?.software === PLEROMA && action.withRedraft && hasIntegerMediaIds(action.status)) {
compose.media_attachments = ImmutableList(); compose.media_attachments = [];
} else { } else {
compose.media_attachments = ImmutableList(action.status.media_attachments); compose.media_attachments = action.status.media_attachments;
} }
if (action.status.spoiler_text.length > 0) { if (action.status.spoiler_text.length > 0) {
@ -661,10 +658,10 @@ const compose = (state = initialState, action: ComposeAction | EventsAction | In
case COMPOSE_CHANGE_MEDIA_ORDER: case COMPOSE_CHANGE_MEDIA_ORDER:
return updateCompose(state, action.composeId, compose => { return updateCompose(state, action.composeId, compose => {
const indexA = compose.media_attachments.findIndex(x => x.id === action.a); const indexA = compose.media_attachments.findIndex(x => x.id === action.a);
const moveItem = compose.media_attachments.get(indexA)!; const moveItem = compose.media_attachments[indexA];
const indexB = compose.media_attachments.findIndex(x => x.id === action.b); const indexB = compose.media_attachments.findIndex(x => x.id === action.b);
return compose.media_attachments.splice(indexA, 1).splice(indexB, 0, moveItem); compose.media_attachments = compose.media_attachments.splice(indexA, 1).splice(indexB, 0, moveItem);
}); });
case COMPOSE_ADD_SUGGESTED_QUOTE: case COMPOSE_ADD_SUGGESTED_QUOTE:
return updateCompose(state, action.composeId, compose => { return updateCompose(state, action.composeId, compose => {