kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Last checkpoint
rodzic
5ea2939f6e
commit
44aba093c0
|
@ -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} />}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 }) => ({
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 = (
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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() }));
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -20,6 +20,7 @@ interface IConfirmationModal {
|
|||
onCancel: () => void;
|
||||
checkbox?: JSX.Element;
|
||||
confirmationTheme?: ButtonThemes;
|
||||
baka: boolean;
|
||||
}
|
||||
|
||||
const ConfirmationModal: React.FC<IConfirmationModal> = ({
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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);
|
||||
|
||||
};
|
||||
|
|
|
@ -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: '',
|
||||
|
|
|
@ -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: '',
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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>;
|
||||
|
|
Ładowanie…
Reference in New Issue