refactor: remove immutable, convert to use plain javascript object

all changes were made to the StatusMedia component and its components, including all subcomponents recursively.
other components that dispatch openModal() of 'MEDIA' still need to be updated
finish-refactor-pure-status-component
P. Reis 2024-12-10 21:20:12 -03:00
rodzic 77e45dea2c
commit 10ff18e76f
22 zmienionych plików z 99 dodań i 100 usunięć

Wyświetl plik

@ -3,12 +3,10 @@ import { Suspense } from 'react';
import { openModal } from 'soapbox/actions/modals.ts';
import { MediaGallery } from 'soapbox/features/ui/util/async-components.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import type { List as ImmutableList } from 'immutable';
import type { Attachment } from 'soapbox/types/entities.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
interface IAttachmentThumbs {
media: ImmutableList<Attachment>;
media: readonly Attachment[];
onClick?(): void;
sensitive?: boolean;
}
@ -18,7 +16,7 @@ const AttachmentThumbs = (props: IAttachmentThumbs) => {
const dispatch = useAppDispatch();
const fallback = <div className='!h-[50px] bg-transparent' />;
const onOpenMedia = (media: ImmutableList<Attachment>, index: number) => dispatch(openModal('MEDIA', { media, index }));
const onOpenMedia = (media: readonly Attachment[], index: number) => dispatch(openModal('MEDIA', { media, index }));
return (
<div className='relative'>

Wyświetl plik

@ -9,13 +9,12 @@ import { closeDropdownMenu as closeDropdownMenuRedux, openDropdownMenu } from 's
import { closeModal, openModal } from 'soapbox/actions/modals.ts';
import IconButton from 'soapbox/components/ui/icon-button.tsx';
import Portal from 'soapbox/components/ui/portal.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { userTouching } from 'soapbox/is-mobile.ts';
import DropdownMenuItem, { MenuItem } from './dropdown-menu-item.tsx';
import type { Status } from 'soapbox/types/entities.ts';
export type Menu = Array<MenuItem | null>;
interface IDropdownMenu {
@ -28,7 +27,7 @@ interface IDropdownMenu {
onShiftClick?: React.EventHandler<React.MouseEvent | React.KeyboardEvent>;
placement?: Placement;
src?: string;
status?: Status;
status?: EntityTypes[Entities.STATUSES];
title?: string;
}

Wyświetl plik

@ -8,7 +8,7 @@ import StillImage from 'soapbox/components/still-image.tsx';
import { MIMETYPE_ICONS } from 'soapbox/components/upload.tsx';
import { useSettings } from 'soapbox/hooks/useSettings.ts';
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
import { Attachment } from 'soapbox/types/entities.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
import { truncateFilename } from 'soapbox/utils/media.ts';
import { isIOS } from '../is-mobile.ts';
@ -16,8 +16,6 @@ import { isPanoramic, isPortrait, isNonConformingRatio, minimumAspectRatio, maxi
import SvgIcon from './ui/svg-icon.tsx';
import type { List as ImmutableList } from 'immutable';
// const Gameboy = lazy(() => import('./gameboy'));
const ATTACHMENT_LIMIT = 4;
@ -46,7 +44,8 @@ const withinLimits = (aspectRatio: number) => {
};
const shouldLetterbox = (attachment: Attachment): boolean => {
const aspectRatio = attachment.getIn(['meta', 'original', 'aspect']) as number | undefined;
const aspectRatio = 'meta' in attachment && 'original' in attachment.meta && (attachment)?.meta?.original?.aspect;
if (!aspectRatio) return true;
return !withinLimits(aspectRatio);
@ -159,7 +158,7 @@ const Item: React.FC<IItem> = ({
const attachmentIcon = (
<SvgIcon
className={clsx('size-16 text-gray-800 dark:text-gray-200', { 'size-8': compact })}
src={MIMETYPE_ICONS[attachment.getIn(['pleroma', 'mime_type']) as string] || paperclipIcon}
src={MIMETYPE_ICONS[attachment?.pleroma?.mime_type as string] || paperclipIcon}
/>
);
@ -291,9 +290,9 @@ const Item: React.FC<IItem> = ({
export interface IMediaGallery {
sensitive?: boolean;
media: ImmutableList<Attachment>;
media: readonly Attachment[];
height?: number;
onOpenMedia: (media: ImmutableList<Attachment>, index: number) => void;
onOpenMedia: (media: readonly Attachment[], index: number) => void;
defaultWidth?: number;
cacheWidth?: (width: number) => void;
visible?: boolean;
@ -323,7 +322,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
const getSizeDataSingle = (): SizeData => {
const w = width || defaultWidth;
const aspectRatio = media.getIn([0, 'meta', 'original', 'aspect']) as number | undefined;
const aspectRatio = 'meta' in media[0] && 'original' in media[0].meta && (media[0])?.meta?.original?.aspect;
const getHeight = () => {
if (!aspectRatio) return w * 9 / 16;
@ -349,7 +348,9 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
let itemsDimensions: Dimensions[] = [];
const ratios = Array(size).fill(null).map((_, i) =>
media.getIn([i, 'meta', 'original', 'aspect']) as number,
'meta' in media[i] && 'original' in media[i].meta && typeof media[i].meta?.original?.aspect === 'number'
? media[i].meta.original.aspect
: undefined as unknown as number, // NOTE: the old logic returned undefined anyways, and the implementation of the functions below call 'isNaN', such as the 'isPortrait' function
);
const [ar1, ar2, ar3, ar4] = ratios;
@ -547,9 +548,9 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
};
};
const sizeData: SizeData = getSizeData(media.size);
const sizeData: SizeData = getSizeData(media.length);
const children = media.take(ATTACHMENT_LIMIT).map((attachment, i) => (
const children = media.slice(0, ATTACHMENT_LIMIT).map((attachment, i) => (
<Item
key={attachment.id}
onClick={handleClick}
@ -560,7 +561,7 @@ const MediaGallery: React.FC<IMediaGallery> = (props) => {
visible={!!props.visible}
dimensions={sizeData.itemsDimensions[i]}
last={i === ATTACHMENT_LIMIT - 1}
total={media.size}
total={media.length}
compact={compact}
/>
));

Wyświetl plik

@ -3,7 +3,6 @@ import linkIcon from '@tabler/icons/outline/link.svg';
import playerPlayIcon from '@tabler/icons/outline/player-play.svg';
import zoomInIcon from '@tabler/icons/outline/zoom-in.svg';
import clsx from 'clsx';
import { List as ImmutableList } from 'immutable';
import { useState, useEffect, useRef } from 'react';
import Blurhash from 'soapbox/components/blurhash.tsx';
@ -12,17 +11,18 @@ import Stack from 'soapbox/components/ui/stack.tsx';
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { normalizeAttachment } from 'soapbox/normalizers/index.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
import { addAutoPlay } from 'soapbox/utils/media.ts';
import { getTextDirection } from 'soapbox/utils/rtl.ts';
import type { Card as CardEntity, Attachment } from 'soapbox/types/entities.ts';
import type { Card as CardEntity } from 'soapbox/types/entities.ts';
/** Props for `PreviewCard`. */
interface IPreviewCard {
card: CardEntity;
maxTitle?: number;
maxDescription?: number;
onOpenMedia: (attachments: ImmutableList<Attachment>, index: number) => void;
onOpenMedia: (attachments: readonly Attachment[], index: number) => void;
compact?: boolean;
defaultWidth?: number;
cacheWidth?: (width: number) => void;
@ -73,9 +73,9 @@ const PreviewCard: React.FC<IPreviewCard> = ({
height: card.height,
},
},
});
}).toJS();
onOpenMedia(ImmutableList([attachment]), 0);
onOpenMedia([{ ...attachment, blurhash: attachment.blurhash === undefined ? null : attachment.blurhash } as Attachment], 0);
};
const handleEmbedClick: React.MouseEventHandler = (e) => {

Wyświetl plik

@ -2,11 +2,10 @@ import circlesIcon from '@tabler/icons/outline/circles.svg';
import pinnedIcon from '@tabler/icons/outline/pinned.svg';
import repeatIcon from '@tabler/icons/outline/repeat.svg';
import clsx from 'clsx';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
import { Link, useHistory } from 'react-router-dom';
import { importFetchedStatuses } from 'soapbox/actions/importer/index.ts';
import { openModal } from 'soapbox/actions/modals.ts';
import { unfilterStatus } from 'soapbox/actions/statuses.ts';
import PureEventPreview from 'soapbox/components/pure-event-preview.tsx';
@ -23,14 +22,14 @@ import { EntityTypes, Entities } from 'soapbox/entity-store/entities.ts';
import QuotedStatus from 'soapbox/features/status/containers/quoted-status-container.tsx';
import { HotKeys } from 'soapbox/features/ui/components/hotkeys.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useFavourite } from 'soapbox/hooks/useFavourite.ts';
import { useMentionCompose } from 'soapbox/hooks/useMentionCompose.ts';
import { useReblog } from 'soapbox/hooks/useReblog.ts';
import { useReplyCompose } from 'soapbox/hooks/useReplyCompose.ts';
import { useSettings } from 'soapbox/hooks/useSettings.ts';
import { useStatusHidden } from 'soapbox/hooks/useStatusHidden.ts';
import { makeGetStatus } from 'soapbox/selectors/index.ts';
import { normalizeStatus } from 'soapbox/normalizers/index.ts';
import { Status } from 'soapbox/types/entities.ts';
import { emojifyText } from 'soapbox/utils/emojify.tsx';
import { defaultMediaVisibility, textForScreenReader, getActualStatus } from 'soapbox/utils/status.ts';
@ -127,16 +126,7 @@ const PureStatus: React.FC<IPureStatus> = (props) => {
}
}, [overlay.current]);
// TODO: remove this code, it will be removed once all components in this file are pure.
useEffect(() => {
dispatch(importFetchedStatuses([status]));
}, []);
const getStatus = useCallback(makeGetStatus(), []);
const statusImmutable = useAppSelector(state => getStatus(state, { id: status.id }));
if (!statusImmutable) {
return null;
}
// END TODO
const statusImmutable = normalizeStatus(status) as Status; // TODO: remove this line, it will be removed once all components in this file are pure.
const handleToggleMediaVisibility = (): void => {
setShowMedia(!showMedia);
@ -486,7 +476,7 @@ const PureStatus: React.FC<IPureStatus> = (props) => {
{(quote || actualStatus.card || actualStatus.media_attachments.length > 0) && (
<Stack space={4}>
<StatusMedia
status={statusImmutable} // FIXME: stop using 'statusImmutable' and use 'status' variable directly, for that create a new component called 'PureStatusMedia'
status={status}
muted={muted}
onClick={handleClick}
showMedia={showMedia}

Wyświetl plik

@ -7,6 +7,7 @@ import { useHistory } from 'react-router-dom';
import StatusMedia from 'soapbox/components/status-media.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import AccountContainer from 'soapbox/containers/account-container.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import { useSettings } from 'soapbox/hooks/useSettings.ts';
import { defaultMediaVisibility } from 'soapbox/utils/status.ts';
@ -138,7 +139,7 @@ const QuotedStatus: React.FC<IQuotedStatus> = ({ status, onCancel, compose }) =>
{status.media_attachments.size > 0 && (
<StatusMedia
status={status}
status={status.toJS() as EntityTypes[Entities.STATUSES]}
muted={compose}
showMedia={showMedia}
onToggleVisibility={handleToggleMediaVisibility}

Wyświetl plik

@ -49,6 +49,7 @@ import DropdownMenu from 'soapbox/components/dropdown-menu/index.ts';
import StatusActionButton from 'soapbox/components/status-action-button.tsx';
import StatusReactionWrapper from 'soapbox/components/status-reaction-wrapper.tsx';
import HStack from 'soapbox/components/ui/hstack.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
@ -826,7 +827,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
/>
)}
<DropdownMenu items={menu} status={status}>
<DropdownMenu items={menu} status={status.toJS() as EntityTypes[Entities.STATUSES]}>
<StatusActionButton
title={intl.formatMessage(messages.more)}
icon={dotsIcon}

Wyświetl plik

@ -4,17 +4,16 @@ import { Suspense } from 'react';
import { openModal } from 'soapbox/actions/modals.ts';
import AttachmentThumbs from 'soapbox/components/attachment-thumbs.tsx';
import PreviewCard from 'soapbox/components/preview-card.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import { GroupLinkPreview } from 'soapbox/features/groups/components/group-link-preview.tsx';
import PlaceholderCard from 'soapbox/features/placeholder/components/placeholder-card.tsx';
import { MediaGallery, Video, Audio } from 'soapbox/features/ui/util/async-components.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import type { List as ImmutableList } from 'immutable';
import type { Status, Attachment } from 'soapbox/types/entities.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
interface IStatusMedia {
/** Status entity to render media for. */
status: Status;
status: EntityTypes[Entities.STATUSES];
/** Whether to display compact media. */
muted?: boolean;
/** Callback when compact media is clicked. */
@ -35,8 +34,8 @@ const StatusMedia: React.FC<IStatusMedia> = ({
}) => {
const dispatch = useAppDispatch();
const size = status.media_attachments.size;
const firstAttachment = status.media_attachments.first();
const size = status.media_attachments.length;
const firstAttachment = status.media_attachments[0];
let media: JSX.Element | null = null;
@ -52,7 +51,7 @@ const StatusMedia: React.FC<IStatusMedia> = ({
return <div className='relative mt-2 block cursor-pointer border-0 bg-cover bg-center bg-no-repeat' style={{ height: '285px' }} />;
};
const openMedia = (media: ImmutableList<Attachment>, index: number) => {
const openMedia = (media: readonly Attachment[], index: number) => {
dispatch(openModal('MEDIA', { media, status, index }));
};
@ -72,10 +71,10 @@ const StatusMedia: React.FC<IStatusMedia> = ({
<Suspense fallback={renderLoadingVideoPlayer()}>
<Video
preview={video.preview_url}
blurhash={video.blurhash}
blurhash={video.blurhash ?? undefined}
src={video.url}
alt={video.description}
aspectRatio={Number(video.meta.getIn(['original', 'aspect']))}
aspectRatio={Number(video.meta?.original?.aspect)}
height={285}
visible={showMedia}
inline
@ -90,11 +89,11 @@ const StatusMedia: React.FC<IStatusMedia> = ({
<Audio
src={attachment.url}
alt={attachment.description}
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : status.getIn(['account', 'avatar_static']) as string | undefined}
backgroundColor={attachment.meta.getIn(['colors', 'background']) as string | undefined}
foregroundColor={attachment.meta.getIn(['colors', 'foreground']) as string | undefined}
accentColor={attachment.meta.getIn(['colors', 'accent']) as string | undefined}
duration={attachment.meta.getIn(['original', 'duration'], 0) as number | undefined}
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : status.account.avatar_static}
backgroundColor={attachment.meta?.colors?.background}
foregroundColor={attachment.meta?.colors?.foreground}
accentColor={attachment.meta?.colors?.accent}
duration={attachment.meta?.duration ?? 0}
height={263}
/>
</Suspense>

Wyświetl plik

@ -16,6 +16,7 @@ import Icon from 'soapbox/components/ui/icon.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import AccountContainer from 'soapbox/containers/account-container.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import QuotedStatus from 'soapbox/features/status/containers/quoted-status-container.tsx';
import { HotKeys } from 'soapbox/features/ui/components/hotkeys.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
@ -465,7 +466,7 @@ const Status: React.FC<IStatus> = (props) => {
{(quote || actualStatus.card || actualStatus.media_attachments.size > 0) && (
<Stack space={4}>
<StatusMedia
status={actualStatus}
status={actualStatus.toJS() as EntityTypes[Entities.STATUSES]}
muted={muted}
onClick={handleClick}
showMedia={showMedia}

Wyświetl plik

@ -9,6 +9,7 @@ import StatusContent from 'soapbox/components/status-content.tsx';
import StatusMedia from 'soapbox/components/status-media.tsx';
import HStack from 'soapbox/components/ui/hstack.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import type { AdminReport, Status } from 'soapbox/types/entities.ts';
@ -52,7 +53,7 @@ const ReportStatus: React.FC<IReportStatus> = ({ status }) => {
<HStack space={2} alignItems='start'>
<Stack space={2} className='overflow-hidden' grow>
<StatusContent status={status} />
<StatusMedia status={status} />
<StatusMedia status={status.toJS() as EntityTypes[Entities.STATUSES]} />
</Stack>
<div className='flex-none'>

Wyświetl plik

@ -23,6 +23,7 @@ import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
import { ChatKeys, IChat, useChatActions } from 'soapbox/queries/chats.ts';
import { queryClient } from 'soapbox/queries/client.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
import { htmlToPlaintext } from 'soapbox/utils/html.ts';
import { isOnlyEmoji as _isOnlyEmoji } from 'soapbox/utils/only-emoji.ts';
@ -112,7 +113,7 @@ const ChatMessage = (props: IChatMessage) => {
'rounded-br-sm': isMyMessage && content,
'rounded-bl-sm': !isMyMessage && content,
})}
media={chatMessage.media_attachments}
media={chatMessage.media_attachments.toJS() as unknown as Attachment[]}
onOpenMedia={onOpenMedia}
visible
/>

Wyświetl plik

@ -5,13 +5,12 @@ import AttachmentThumbs from 'soapbox/components/attachment-thumbs.tsx';
import Markup from 'soapbox/components/markup.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import AccountContainer from 'soapbox/containers/account-container.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import { getTextDirection } from 'soapbox/utils/rtl.ts';
import type { Status } from 'soapbox/types/entities.ts';
interface IReplyIndicator {
className?: string;
status?: Status;
status?: EntityTypes[Entities.STATUSES];
onCancel?: () => void;
hideActions: boolean;
}
@ -50,12 +49,12 @@ const ReplyIndicator: React.FC<IReplyIndicator> = ({ className, status, hideActi
className='break-words'
size='sm'
direction={getTextDirection(status.search_index)}
emojis={status?.emojis?.toJS() ?? status.emojis} // Use toJS() if status.emojis is immutable; otherwise, fallback to plain status.emojis
mentions={status.mentions.toJS()}
emojis={status.emojis}
mentions={status.mentions}
html={{ __html: status.content }}
/>
{status.media_attachments.size > 0 && (
{status.media_attachments.length > 0 && (
<AttachmentThumbs
media={status.media_attachments}
sensitive={status.sensitive}

Wyświetl plik

@ -1,6 +1,7 @@
import { connect } from 'react-redux';
import { cancelReplyCompose } from 'soapbox/actions/compose.ts';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import { makeGetStatus } from 'soapbox/selectors/index.ts';
import ReplyIndicator from '../components/reply-indicator.tsx';
@ -16,7 +17,7 @@ const makeMapStateToProps = () => {
const editing = !!state.compose.get(composeId)?.id;
return {
status: getStatus(state, { id: statusId }) as Status,
status: (getStatus(state, { id: statusId }) as Status)?.toJS() as EntityTypes[Entities.STATUSES],
hideActions: editing,
};
};

Wyświetl plik

@ -15,6 +15,7 @@ import HStack from 'soapbox/components/ui/hstack.tsx';
import Icon from 'soapbox/components/ui/icon.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import QuotedStatus from 'soapbox/features/status/containers/quoted-status-container.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
@ -208,7 +209,7 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
)}
<StatusMedia
status={status}
status={status.toJS() as EntityTypes[Entities.STATUSES]}
showMedia={showMedia}
onToggleVisibility={handleToggleMediaVisibility}
/>

Wyświetl plik

@ -7,6 +7,7 @@ import Toggle from 'soapbox/components/ui/toggle.tsx';
import { MediaGallery, Video, Audio } from 'soapbox/features/ui/util/async-components.ts';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
interface IStatusCheckBox {
id: string;
@ -62,7 +63,7 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
} else {
media = (
<MediaGallery
media={status.media_attachments}
media={status.media_attachments.toJS() as unknown as Attachment[]}
sensitive={status.sensitive}
height={110}
onOpenMedia={() => {}}

Wyświetl plik

@ -8,6 +8,7 @@ import HStack from 'soapbox/components/ui/hstack.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import PollPreview from 'soapbox/features/ui/components/poll-preview.tsx';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
import { buildStatus } from '../builder.tsx';
@ -55,7 +56,7 @@ const ScheduledStatus: React.FC<IScheduledStatus> = ({ statusId, ...other }) =>
{status.media_attachments.size > 0 && (
<AttachmentThumbs
media={status.media_attachments}
media={status.media_attachments.toJS() as unknown as Attachment[]}
sensitive={status.sensitive}
/>
)}

Wyświetl plik

@ -16,6 +16,7 @@ import HStack from 'soapbox/components/ui/hstack.tsx';
import Icon from 'soapbox/components/ui/icon.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import QuotedStatus from 'soapbox/features/status/containers/quoted-status-container.tsx';
import { getActualStatus } from 'soapbox/utils/status.ts';
@ -162,7 +163,7 @@ const DetailedStatus: React.FC<IDetailedStatus> = ({
{(withMedia && (quote || actualStatus.card || actualStatus.media_attachments.size > 0)) && (
<Stack space={4}>
<StatusMedia
status={actualStatus}
status={actualStatus.toJS() as EntityTypes[Entities.STATUSES]}
showMedia={showMedia}
onToggleVisibility={onToggleMediaVisibility}
/>

Wyświetl plik

@ -4,15 +4,15 @@ import { spring } from 'react-motion';
import HStack from 'soapbox/components/ui/hstack.tsx';
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import ReplyIndicator from 'soapbox/features/compose/components/reply-indicator.tsx';
import Motion from '../../util/optional-motion.tsx';
import type { Menu, MenuItem } from 'soapbox/components/dropdown-menu/index.ts';
import type { Status as StatusEntity } from 'soapbox/types/entities.ts';
interface IActionsModal {
status: StatusEntity;
status: EntityTypes[Entities.STATUSES];
actions: Menu;
onClick: () => void;
onClose: () => void;

Wyświetl plik

@ -5,6 +5,7 @@ import Icon from 'soapbox/components/icon.tsx';
import Modal from 'soapbox/components/ui/modal.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import ReplyIndicator from 'soapbox/features/compose/components/reply-indicator.tsx';
import type { Status as StatusEntity } from 'soapbox/types/entities.ts';
@ -37,7 +38,7 @@ const BoostModal: React.FC<IBoostModal> = ({ status, onReblog, onClose }) => {
confirmationText={intl.formatMessage(buttonText)}
>
<Stack space={4}>
<ReplyIndicator status={status} hideActions />
<ReplyIndicator status={status.toJS() as EntityTypes[Entities.STATUSES]} hideActions />
<Text>
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}

Wyświetl plik

@ -12,6 +12,7 @@ import Stack from 'soapbox/components/ui/stack.tsx';
import Text from 'soapbox/components/ui/text.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
import { emojifyText } from 'soapbox/utils/emojify.tsx';
import type { StatusEdit as StatusEditEntity } from 'soapbox/types/entities.ts';
@ -81,7 +82,7 @@ const CompareHistoryModal: React.FC<ICompareHistoryModal> = ({ onClose, statusId
)}
{version.media_attachments.size > 0 && (
<AttachmentThumbs media={version.media_attachments} />
<AttachmentThumbs media={version.media_attachments.toJS() as unknown as Attachment[]} />
)}
<Text align='right' tag='span' theme='muted' size='sm'>

Wyświetl plik

@ -19,20 +19,20 @@ import HStack from 'soapbox/components/ui/hstack.tsx';
import IconButton from 'soapbox/components/ui/icon-button.tsx';
import Icon from 'soapbox/components/ui/icon.tsx';
import Stack from 'soapbox/components/ui/stack.tsx';
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
import Audio from 'soapbox/features/audio/index.tsx';
import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder-status.tsx';
import Thread from 'soapbox/features/status/components/thread.tsx';
import Video from 'soapbox/features/video/index.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { userTouching } from 'soapbox/is-mobile.ts';
import { makeGetStatus } from 'soapbox/selectors/index.ts';
import { normalizeStatus } from 'soapbox/normalizers/index.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
import { Status } from 'soapbox/types/entities.ts';
import { getActualStatus } from 'soapbox/utils/status.ts';
import ImageLoader from '../image-loader.tsx';
import type { List as ImmutableList } from 'immutable';
import type { Attachment, Status } from 'soapbox/types/entities.ts';
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
expand: { id: 'lightbox.expand', defaultMessage: 'Expand' },
@ -55,8 +55,8 @@ const containerStyle: React.CSSProperties = {
};
interface IMediaModal {
media: ImmutableList<Attachment>;
status?: Status;
media: readonly Attachment[];
status?: EntityTypes[Entities.STATUSES];
index: number;
time?: number;
onClose(): void;
@ -74,8 +74,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
const history = useHistory();
const intl = useIntl();
const getStatus = useCallback(makeGetStatus(), []);
const actualStatus = useAppSelector((state) => getStatus(state, { id: status?.id as string }));
const actualStatus = status ? getActualStatus(status) : undefined;
const [isLoaded, setIsLoaded] = useState<boolean>(!!status);
const [next, setNext] = useState<string>();
@ -83,11 +82,11 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
const [navigationHidden, setNavigationHidden] = useState(false);
const [isFullScreen, setIsFullScreen] = useState(!status);
const hasMultipleImages = media.size > 1;
const hasMultipleImages = media.length > 1;
const handleSwipe = (index: number) => setIndex(index % media.size);
const handleNextClick = () => setIndex((getIndex() + 1) % media.size);
const handlePrevClick = () => setIndex((media.size + getIndex() - 1) % media.size);
const handleSwipe = (index: number) => setIndex(index % media.length);
const handleNextClick = () => setIndex((getIndex() + 1) % media.length);
const handlePrevClick = () => setIndex((media.length + getIndex() - 1) % media.length);
const navigationHiddenClassName = navigationHidden ? 'pointer-events-none opacity-0' : '';
@ -107,7 +106,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
};
const handleDownload = () => {
const mediaItem = hasMultipleImages ? media.get(index as number) : media.get(0);
const mediaItem = hasMultipleImages ? media[index as number] : media[0];
window.open(mediaItem?.url);
};
@ -126,8 +125,8 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
};
const content = media.map((attachment, i) => {
const width = (attachment.meta.getIn(['original', 'width']) || undefined) as number | undefined;
const height = (attachment.meta.getIn(['original', 'height']) || undefined) as number | undefined;
const width = 'meta' in attachment && 'original' in attachment.meta ? (attachment)?.meta?.original?.width : undefined;
const height = 'meta' in attachment && 'original' in attachment.meta ? (attachment)?.meta?.original?.height : undefined;
const link = (status && (
<a href={status.url} onClick={handleStatusClick}>
@ -151,7 +150,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
return (
<Video
preview={attachment.preview_url}
blurhash={attachment.blurhash}
blurhash={attachment.blurhash ?? undefined}
src={attachment.url}
width={width}
height={height}
@ -169,11 +168,11 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
<Audio
src={attachment.url}
alt={attachment.description}
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : (status?.getIn(['account', 'avatar_static'])) as string | undefined}
backgroundColor={attachment.meta.getIn(['colors', 'background']) as string | undefined}
foregroundColor={attachment.meta.getIn(['colors', 'foreground']) as string | undefined}
accentColor={attachment.meta.getIn(['colors', 'accent']) as string | undefined}
duration={attachment.meta.getIn(['original', 'duration'], 0) as number | undefined}
poster={attachment.preview_url !== attachment.url ? attachment.preview_url : status?.account.avatar_static}
backgroundColor={attachment.meta?.colors?.background}
foregroundColor={attachment.meta?.colors?.foreground}
accentColor={attachment.meta?.colors?.accent}
duration={attachment?.meta?.duration ?? 0}
key={attachment.url}
/>
);
@ -193,7 +192,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
}
return null;
}).toArray();
});
const handleLoadMore = useCallback(debounce(() => {
if (next && status) {
@ -344,7 +343,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
className={clsx('absolute bottom-2 flex w-full transition-opacity', navigationHiddenClassName)}
>
<StatusActionBar
status={actualStatus}
status={normalizeStatus(actualStatus) as Status}
space='md'
statusActionButtonTheme='inverse'
/>
@ -362,7 +361,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
}
>
<Thread
status={actualStatus}
status={normalizeStatus(actualStatus) as Status}
withMedia={false}
useWindowScroll={false}
itemClassName='px-4'

Wyświetl plik

@ -21,11 +21,13 @@ import AccountContainer from 'soapbox/containers/account-container.tsx';
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
import { useInstance } from 'soapbox/hooks/useInstance.ts';
import { Attachment } from 'soapbox/schemas/index.ts';
import ConfirmationStep from './steps/confirmation-step.tsx';
import OtherActionsStep from './steps/other-actions-step.tsx';
import ReasonStep from './steps/reason-step.tsx';
const messages = defineMessages({
blankslate: { id: 'report.reason.blankslate', defaultMessage: 'You have removed all statuses from being selected.' },
done: { id: 'report.done', defaultMessage: 'Done' },
@ -91,7 +93,7 @@ const SelectedStatus = ({ statusId }: { statusId: string }) => {
{status.media_attachments.size > 0 && (
<AttachmentThumbs
media={status.media_attachments}
media={status.media_attachments.toJS() as unknown as Attachment[]}
sensitive={status.sensitive}
/>
)}