Last checkpoint

update-compose
danidfra 2024-12-13 18:16:23 -03:00
rodzic 5ea2939f6e
commit 44aba093c0
25 zmienionych plików z 87 dodań i 67 usunięć

Wyświetl plik

@ -101,7 +101,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
const hasPoll = !!compose.poll;
const isEditing = compose.id !== null;
const anyMedia = compose.media_attachments.size > 0;
const anyMedia = compose.media_attachments.length > 0;
const [composeFocused, setComposeFocused] = useState(false);
@ -260,7 +260,9 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
{!shouldCondense && !event && !group && groupId && <ReplyGroupIndicator composeId={id} />}
{!shouldCondense && !event && !group && <ReplyIndicatorContainer composeId={id} />}
{!shouldCondense && !event && !group && <ReplyIndicatorContainer
composeId={id}
/>}
{!shouldCondense && !event && !group && <ReplyMentions composeId={id} />}

Wyświetl plik

@ -42,7 +42,7 @@ const UploadForm: React.FC<IUploadForm> = ({ composeId, onSubmit }) => {
<div className='overflow-hidden'>
<UploadProgress composeId={composeId} />
<HStack wrap className={clsx('overflow-hidden', mediaIds.size !== 0 && 'p-1')}>
<HStack wrap className={clsx('overflow-hidden', mediaIds.length !== 0 && 'p-1')}>
{mediaIds.map((id: string) => (
<Upload
id={id}

Wyświetl plik

@ -15,7 +15,10 @@ const QuotedStatusContainer: React.FC<IQuotedStatusContainer> = ({ composeId })
const dispatch = useAppDispatch();
const getStatus = useCallback(makeGetStatus(), []);
const status = useAppSelector(state => getStatus(state, { id: state.compose.composeId.quote! }));
const status = useAppSelector(state => {
const compose = state.compose[composeId];
return compose ? getStatus(state, { id: compose.quote! }) : null;
});
const onCancel = () => {
dispatch(cancelQuoteCompose());

Wyświetl plik

@ -8,8 +8,8 @@ import type { IntlShape } from 'react-intl';
import type { AppDispatch, RootState } from 'soapbox/store.ts';
const mapStateToProps = (state: RootState, { composeId }: { composeId: string }) => ({
disabled: state.compose.get(composeId)?.is_uploading,
resetFileKey: state.compose.get(composeId)?.resetFileKey!,
disabled: state.compose[composeId]?.is_uploading,
resetFileKey: state.compose[composeId]?.resetFileKey!,
});
const mapDispatchToProps = (dispatch: AppDispatch, { composeId }: { composeId: string }) => ({

Wyświetl plik

@ -94,7 +94,7 @@ const ComposeEditor = forwardRef<LexicalEditor, IComposeEditor>(({
theme,
editorState: dispatch((_, getState) => {
const state = getState();
const compose = state.compose.get(composeId);
const compose = state.compose.composeId;
if (!compose) return;

Wyświetl plik

@ -297,7 +297,7 @@ const AutosuggestPlugin = ({
};
const onSelectSuggestion = (index: number) => {
const suggestion = suggestions.get(index) as AutoSuggestion;
const suggestion = suggestions[index] as AutoSuggestion;
editor.update(() => {
dispatch((dispatch, getState) => {
@ -443,11 +443,11 @@ const AutosuggestPlugin = ({
]);
useEffect(() => {
if (suggestions && suggestions.size > 0) setSuggestionsHidden(false);
if (suggestions && suggestions.length > 0) setSuggestionsHidden(false);
}, [suggestions]);
useEffect(() => {
if (resolution !== null && !suggestionsHidden && !suggestions.isEmpty()) {
if (resolution !== null && !suggestionsHidden && suggestions.length > 0) {
const handleClick = (event: MouseEvent) => {
const target = event.target as HTMLElement;
@ -459,7 +459,7 @@ const AutosuggestPlugin = ({
return () => document.removeEventListener('click', handleClick);
}
}, [resolution, suggestionsHidden, suggestions.isEmpty()]);
}, [resolution, suggestionsHidden, suggestions.length === 0]);
useEffect(() => {
if (resolution === null) return;
@ -469,8 +469,8 @@ const AutosuggestPlugin = ({
KEY_ARROW_UP_COMMAND,
(payload) => {
const event = payload;
if (suggestions !== null && suggestions.size && selectedSuggestion !== null) {
const newSelectedSuggestion = selectedSuggestion !== 0 ? selectedSuggestion - 1 : suggestions.size - 1;
if (suggestions !== null && suggestions.length && selectedSuggestion !== null) {
const newSelectedSuggestion = selectedSuggestion !== 0 ? selectedSuggestion - 1 : suggestions.length - 1;
setSelectedSuggestion(newSelectedSuggestion);
event.preventDefault();
event.stopImmediatePropagation();
@ -483,8 +483,8 @@ const AutosuggestPlugin = ({
KEY_ARROW_DOWN_COMMAND,
(payload) => {
const event = payload;
if (suggestions !== null && suggestions.size && selectedSuggestion !== null) {
const newSelectedSuggestion = selectedSuggestion !== suggestions.size - 1 ? selectedSuggestion + 1 : 0;
if (suggestions !== null && suggestions.length && selectedSuggestion !== null) {
const newSelectedSuggestion = selectedSuggestion !== suggestions.length - 1 ? selectedSuggestion + 1 : 0;
setSelectedSuggestion(newSelectedSuggestion);
event.preventDefault();
event.stopImmediatePropagation();
@ -540,8 +540,8 @@ const AutosuggestPlugin = ({
<div
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,
hidden: suggestionsHidden || suggestions.isEmpty(),
block: !suggestionsHidden && !suggestions.isEmpty(),
hidden: suggestionsHidden || suggestions.length === 0,
block: !suggestionsHidden && suggestions.length > 0,
})}
>
{suggestions.map(renderSuggestion)}

Wyświetl plik

@ -32,7 +32,13 @@ const GroupGallery: React.FC<IGroupGallery> = (props) => {
} = useGroupMedia(groupId);
const attachments = statuses.reduce<Attachment[]>((result, status) => {
result.push(...status.media_attachments.map((a) => a.set('status', status)));
const mappedAttachments = status.media_attachments.map((a) => {
return {
...a,
status,
} as Attachment;
});
result.push(...mappedAttachments);
return result;
}, []);
@ -70,7 +76,7 @@ const GroupGallery: React.FC<IGroupGallery> = (props) => {
<div role='feed' className='mt-4 grid grid-cols-2 gap-2 sm:grid-cols-3'>
{attachments.map((attachment) => (
<MediaItem
key={`${attachment.status.id}+${attachment.id}`}
key={`${attachment.status!.id}+${attachment.id}`}
attachment={attachment}
onOpenMedia={handleOpenMedia}
/>

Wyświetl plik

@ -42,7 +42,7 @@ const GroupTimeline: React.FC<IGroupTimeline> = (props) => {
const composeId = `group:${groupId}`;
const canComposeGroupStatus = !!account && group?.relationship?.member;
const groupTimelineVisible = useAppSelector((state) => !!state.compose.get(composeId)?.group_timeline_visible);
const groupTimelineVisible = useAppSelector((state) => !!state.compose.composeId?.group_timeline_visible);
const featuredStatusIds = useAppSelector((state) => getStatusIds(state, { type: `group:${group?.id}:pinned` }));
const { isDragging, isDraggedOver } = useDraggedFiles(composer, (files) => {

Wyświetl plik

@ -1,10 +1,10 @@
import { List as ImmutableList, Record as ImmutableRecord } from 'immutable';
import { Record as ImmutableRecord } from 'immutable';
import { useState } from 'react';
import type { Attachment as AttachmentEntity } from 'soapbox/types/entities.ts';
interface IPlaceholderMediaGallery {
media: ImmutableList<AttachmentEntity>;
media: AttachmentEntity[];
defaultWidth?: number;
}
@ -81,11 +81,11 @@ const PlaceholderMediaGallery: React.FC<IPlaceholderMediaGallery> = ({ media, de
return <div key={i} className='relative float-left box-border block animate-pulse overflow-hidden rounded-sm border-0 bg-primary-200' style={{ position, float, left, top, right, bottom, height, width }} />;
};
const sizeData = getSizeData(media.size);
const sizeData = getSizeData(media.length);
return (
<div className='relative isolate box-border h-auto w-full overflow-hidden rounded-lg' style={sizeData.get('style')} ref={handleRef}>
{media.take(4).map((_, i) => renderItem(sizeData.get('itemsDimensions')[i], i))}
{media.slice(0, 4).map((_, i) => renderItem(sizeData.get('itemsDimensions')[i], i))}
</div>
);
};

Wyświetl plik

@ -21,7 +21,7 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
const onToggle: React.ChangeEventHandler<HTMLInputElement> = (e) => dispatch(toggleStatusReport(id, e.target.checked));
const mediaType = status?.media_attachments.get(0)?.type;
const mediaType = status?.media_attachments[0].type;
if (!status || status.reblog) {
return null;
@ -29,11 +29,11 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
let media;
if (status.media_attachments.size > 0) {
if (status.media_attachments.length > 0) {
if (status.media_attachments.some(item => item.type === 'unknown')) {
// Do nothing
} else if (status.media_attachments.get(0)?.type === 'video') {
const video = status.media_attachments.get(0);
} else if (status.media_attachments[0].type === 'video') {
const video = status.media_attachments[0];
if (video) {
media = (
@ -49,8 +49,8 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
/>
);
}
} else if (status.media_attachments.get(0)?.type === 'audio') {
const audio = status.media_attachments.get(0);
} else if (status.media_attachments[0].type === 'audio') {
const audio = status.media_attachments[0];
if (audio) {
media = (

Wyświetl plik

@ -54,7 +54,7 @@ const ScheduledStatus: React.FC<IScheduledStatus> = ({ statusId, ...other }) =>
collapsable
/>
{status.media_attachments.size > 0 && (
{status.media_attachments.length > 0 && (
<AttachmentThumbs
media={status.media_attachments.toJS() as unknown as Attachment[]}
sensitive={status.sensitive}

Wyświetl plik

@ -173,10 +173,10 @@ const Thread = (props: IThread) => {
e?.preventDefault();
if (media && media.size) {
const firstAttachment = media.first()!;
if (media && media.length) {
const firstAttachment = media[0];
if (media.size === 1 && firstAttachment.type === 'video') {
if (media.length === 1 && firstAttachment.type === 'video') {
dispatch(openModal('VIDEO', { media: firstAttachment, status: status }));
} else {
dispatch(openModal('MEDIA', { media: media.toJS(), index: 0, status: status.toJS() }));

Wyświetl plik

@ -25,8 +25,8 @@ const ErrorColumn: React.FC<IErrorColumn> = ({ error, onRetry = () => location.r
onRetry?.();
};
if (!isNetworkError(error)) {
throw error;
if (!error || !isNetworkError(error)) {
throw error || new Error('Unknown error occurred');
}
return (

Wyświetl plik

@ -1,4 +1,3 @@
import { List as ImmutableList } from 'immutable';
import { useState, useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
@ -27,13 +26,13 @@ const GroupMediaPanel: React.FC<IGroupMediaPanel> = ({ group }) => {
const isMember = !!group?.relationship?.member;
const isPrivate = group?.locked;
const attachments: ImmutableList<Attachment> = useAppSelector((state) => group ? getGroupGallery(state, group?.id) : ImmutableList());
const attachments: Attachment[] = useAppSelector((state) => group ? getGroupGallery(state, group?.id) : []);
const handleOpenMedia = (attachment: Attachment): void => {
if (attachment.type === 'video') {
dispatch(openModal('VIDEO', { media: attachment, status: attachment.status }));
} else {
const media = attachment.getIn(['status', 'media_attachments']) as ImmutableList<Attachment>;
const media = attachment.status?.media_attachments as Attachment[];
const index = media.findIndex(x => x.id === attachment.id);
dispatch(openModal('MEDIA', { media: media.toJS(), index, status: attachment?.status?.toJS() ?? attachment.status, account: attachment.account })); // NOTE: why 'account' field is here? it doesn't exist in MediaModal component
@ -54,12 +53,12 @@ const GroupMediaPanel: React.FC<IGroupMediaPanel> = ({ group }) => {
const renderAttachments = () => {
const nineAttachments = attachments.slice(0, 9);
if (!nineAttachments.isEmpty()) {
if (nineAttachments.length > 0) {
return (
<div className='grid grid-cols-3 gap-1'>
{nineAttachments.map((attachment, _index) => (
<MediaItem
key={`${attachment.getIn(['status', 'id'])}+${attachment.id}`}
key={`${attachment.status!.id}+${attachment.id}`}
attachment={attachment}
onOpenMedia={handleOpenMedia}
/>

Wyświetl plik

@ -81,8 +81,8 @@ const CompareHistoryModal: React.FC<ICompareHistoryModal> = ({ onClose, statusId
</div>
)}
{version.media_attachments.size > 0 && (
<AttachmentThumbs media={version.media_attachments.toJS() as unknown as Attachment[]} />
{version.media_attachments.length > 0 && (
<AttachmentThumbs media={version.media_attachments as unknown as Attachment[]} />
)}
<Text align='right' tag='span' theme='muted' size='sm'>

Wyświetl plik

@ -107,7 +107,7 @@ const ComposeFormGroupToggle: React.FC<IComposeFormGroupToggle> = ({ composeId,
const dispatch = useAppDispatch();
const { group } = useGroup(groupId || '', false);
const groupTimelineVisible = useAppSelector((state) => !!state.compose.get(composeId)?.group_timeline_visible);
const groupTimelineVisible = useAppSelector((state) => !!state.compose.composeId?.group_timeline_visible);
const handleToggleChange = () => {
dispatch(setGroupTimelineVisible(composeId, !groupTimelineVisible));

Wyświetl plik

@ -20,6 +20,7 @@ interface IConfirmationModal {
onCancel: () => void;
checkbox?: JSX.Element;
confirmationTheme?: ButtonThemes;
baka: boolean;
}
const ConfirmationModal: React.FC<IConfirmationModal> = ({

Wyświetl plik

@ -90,7 +90,7 @@ const SelectedStatus = ({ statusId }: { statusId: string }) => {
collapsable
/>
{status.media_attachments.size > 0 && (
{status.media_attachments.length > 0 && (
<AttachmentThumbs
media={status.media_attachments.toJS() as unknown as Attachment[]}
sensitive={status.sensitive}

Wyświetl plik

@ -33,7 +33,7 @@ interface IPendingStatusMedia {
}
const PendingStatusMedia: React.FC<IPendingStatusMedia> = ({ status }) => {
if (status.media_attachments && !status.media_attachments.isEmpty()) {
if (status.media_attachments && status.media_attachments.length > 0) {
return (
<PlaceholderMediaGallery
media={status.media_attachments}

Wyświetl plik

@ -1,4 +1,3 @@
import { List as ImmutableList } from 'immutable';
import { useState, useEffect } from 'react';
import { FormattedMessage } from 'react-intl';
@ -25,13 +24,13 @@ const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
const [loading, setLoading] = useState(true);
const attachments: ImmutableList<Attachment> = useAppSelector((state) => account ? getAccountGallery(state, account?.id) : ImmutableList());
const attachments: Attachment[] = useAppSelector((state) => account ? getAccountGallery(state, account?.id) : []);
const handleOpenMedia = (attachment: Attachment): void => {
if (attachment.type === 'video') {
dispatch(openModal('VIDEO', { media: attachment, status: attachment.status }));
} else {
const media = attachment.getIn(['status', 'media_attachments']) as ImmutableList<Attachment>;
const media = attachment.status?.media_attachments as Attachment[];
const index = media.findIndex(x => x.id === attachment.id);
dispatch(openModal('MEDIA', { media: media.toJS(), index, status: attachment?.status?.toJS() ?? attachment.status }));
@ -50,15 +49,15 @@ const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
}, [account?.id]);
const renderAttachments = () => {
const publicAttachments = attachments.filter(attachment => attachment.getIn(['status', 'visibility']) === 'public');
const publicAttachments = attachments.filter(attachment => attachment.status?.visibility === 'public');
const nineAttachments = publicAttachments.slice(0, 9);
if (!nineAttachments.isEmpty()) {
if (nineAttachments.length > 0) {
return (
<div className='grid grid-cols-3 gap-1'>
{nineAttachments.map((attachment, _index) => (
<MediaItem
key={`${attachment.getIn(['status', 'id'])}+${attachment.id}`}
key={`${attachment.status!.id}+${attachment.id}`}
attachment={attachment}
onOpenMedia={handleOpenMedia}
/>

Wyświetl plik

@ -1,8 +1,9 @@
import { useAppSelector } from './useAppSelector.ts';
import type { ReducerCompose } from 'soapbox/reducers/compose.ts';
import type { Compose } from 'soapbox/reducers/compose.ts';
/** Get compose for given key with fallback to 'default' */
export const useCompose = <ID extends string>(composeId: ID extends 'default' ? never : ID): ReturnType<typeof ReducerCompose> => {
return useAppSelector((state) => state.compose.get(composeId, state.compose.get('default')!));
export const useCompose = <ID extends string>(composeId: ID extends 'default' ? never : ID): Compose => {
return useAppSelector((state) => state.compose[composeId] || state.compose?.default);
};

Wyświetl plik

@ -13,7 +13,7 @@ import type { Attachment, Card, Emoji, EmojiReaction } from 'soapbox/types/entit
export const ChatMessageRecord = ImmutableRecord({
account_id: '',
media_attachments: ImmutableList<Attachment>(),
media_attachments: [] as Attachment[],
card: null as Card | null,
chat_id: '',
content: '',

Wyświetl plik

@ -22,7 +22,7 @@ export const StatusEditRecord = ImmutableRecord({
created_at: new Date(),
emojis: ImmutableList<Emoji>(),
favourited: false,
media_attachments: ImmutableList<Attachment>(),
media_attachments: [] as Attachment[],
poll: null as EmbeddedEntity<Poll>,
sensitive: false,
spoiler_text: '',

Wyświetl plik

@ -182,31 +182,40 @@ export const makeGetNotification = () => {
};
export const getAccountGallery = createSelector([
(state: RootState, id: string) => state.timelines.get(`account:${id}:media`)?.items || ImmutableOrderedSet<string>(),
(state: RootState, id: string) => state.timelines.get(`account:${id}:media`)?.items || new Set<string>(),
(state: RootState) => state.statuses,
], (statusIds, statuses) => {
return statusIds.reduce((medias: ImmutableList<any>, statusId: string) => {
return Array.from(statusIds).reduce((medias: any[], statusId: string) => {
const status = statuses.get(statusId);
if (!status) return medias;
if (status.reblog) return medias;
return medias.concat(
status.media_attachments.map(media => media.merge({ status, account: status.account })));
}, ImmutableList());
status.media_attachments.map(media => ({
...media,
status,
account: status.account,
})));
}, []);
});
export const getGroupGallery = createSelector([
(state: RootState, id: string) => state.timelines.get(`group:${id}:media`)?.items || ImmutableOrderedSet<string>(),
(state: RootState, id: string) => state.timelines.get(`group:${id}:media`)?.items || new Set<string>(),
(state: RootState) => state.statuses,
], (statusIds, statuses) => {
return statusIds.reduce((medias: ImmutableList<any>, statusId: string) => {
return Array.from(statusIds).reduce((medias: any[], statusId: string) => {
const status = statuses.get(statusId);
if (!status) return medias;
if (status.reblog) return medias;
return medias.concat(
status.media_attachments.map(media => media.merge({ status, account: status.account })));
}, ImmutableList());
status.media_attachments.map(media => ({
...media,
status,
account: status.account,
})),
);
}, []);
});
type APIChat = { id: string; last_message: string };

Wyświetl plik

@ -34,8 +34,8 @@ type Attachment = {
remote_url: string | null;
type: string;
url: string;
account?: string | null;
status?: string | null;
account?: Account | null;
status?: Status | null;
};
type Chat = ReturnType<typeof ChatRecord>;
type ChatMessage = ReturnType<typeof ChatMessageRecord>;