From 10ff18e76fb0117b657d1f2cea0d425b12a3377a Mon Sep 17 00:00:00 2001 From: "P. Reis" Date: Tue, 10 Dec 2024 21:20:12 -0300 Subject: [PATCH 1/8] 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 --- src/components/attachment-thumbs.tsx | 8 ++- .../dropdown-menu/dropdown-menu.tsx | 5 +- src/components/media-gallery.tsx | 25 +++++----- src/components/preview-card.tsx | 10 ++-- src/components/pure-status.tsx | 20 ++------ src/components/quoted-status.tsx | 3 +- src/components/status-action-bar.tsx | 3 +- src/components/status-media.tsx | 27 +++++----- src/components/status.tsx | 3 +- .../admin/components/report-status.tsx | 3 +- .../chats/components/chat-message.tsx | 3 +- .../compose/components/reply-indicator.tsx | 11 ++--- .../containers/reply-indicator-container.ts | 3 +- src/features/event/event-information.tsx | 3 +- .../report/components/status-check-box.tsx | 3 +- .../components/scheduled-status.tsx | 3 +- .../status/components/detailed-status.tsx | 3 +- .../ui/components/modals/actions-modal.tsx | 4 +- .../ui/components/modals/boost-modal.tsx | 3 +- .../modals/compare-history-modal.tsx | 3 +- .../ui/components/modals/media-modal.tsx | 49 +++++++++---------- .../modals/report-modal/report-modal.tsx | 4 +- 22 files changed, 99 insertions(+), 100 deletions(-) diff --git a/src/components/attachment-thumbs.tsx b/src/components/attachment-thumbs.tsx index 4829b7688..2b4aadf75 100644 --- a/src/components/attachment-thumbs.tsx +++ b/src/components/attachment-thumbs.tsx @@ -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; + media: readonly Attachment[]; onClick?(): void; sensitive?: boolean; } @@ -18,7 +16,7 @@ const AttachmentThumbs = (props: IAttachmentThumbs) => { const dispatch = useAppDispatch(); const fallback =
; - const onOpenMedia = (media: ImmutableList, index: number) => dispatch(openModal('MEDIA', { media, index })); + const onOpenMedia = (media: readonly Attachment[], index: number) => dispatch(openModal('MEDIA', { media, index })); return (
diff --git a/src/components/dropdown-menu/dropdown-menu.tsx b/src/components/dropdown-menu/dropdown-menu.tsx index 161d92307..84af570bb 100644 --- a/src/components/dropdown-menu/dropdown-menu.tsx +++ b/src/components/dropdown-menu/dropdown-menu.tsx @@ -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; interface IDropdownMenu { @@ -28,7 +27,7 @@ interface IDropdownMenu { onShiftClick?: React.EventHandler; placement?: Placement; src?: string; - status?: Status; + status?: EntityTypes[Entities.STATUSES]; title?: string; } diff --git a/src/components/media-gallery.tsx b/src/components/media-gallery.tsx index 085d95e61..9b59bab2d 100644 --- a/src/components/media-gallery.tsx +++ b/src/components/media-gallery.tsx @@ -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 = ({ const attachmentIcon = ( ); @@ -291,9 +290,9 @@ const Item: React.FC = ({ export interface IMediaGallery { sensitive?: boolean; - media: ImmutableList; + media: readonly Attachment[]; height?: number; - onOpenMedia: (media: ImmutableList, 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 = (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 = (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 = (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) => ( = (props) => { visible={!!props.visible} dimensions={sizeData.itemsDimensions[i]} last={i === ATTACHMENT_LIMIT - 1} - total={media.size} + total={media.length} compact={compact} /> )); diff --git a/src/components/preview-card.tsx b/src/components/preview-card.tsx index c9f70c981..3e8282396 100644 --- a/src/components/preview-card.tsx +++ b/src/components/preview-card.tsx @@ -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, 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 = ({ 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) => { diff --git a/src/components/pure-status.tsx b/src/components/pure-status.tsx index 51e40d0f6..7135c690d 100644 --- a/src/components/pure-status.tsx +++ b/src/components/pure-status.tsx @@ -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 = (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 = (props) => { {(quote || actualStatus.card || actualStatus.media_attachments.length > 0) && ( = ({ status, onCancel, compose }) => {status.media_attachments.size > 0 && ( = ({ /> )} - + = ({ }) => { 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 = ({ return
; }; - const openMedia = (media: ImmutableList, index: number) => { + const openMedia = (media: readonly Attachment[], index: number) => { dispatch(openModal('MEDIA', { media, status, index })); }; @@ -72,10 +71,10 @@ const StatusMedia: React.FC = ({ diff --git a/src/components/status.tsx b/src/components/status.tsx index dc36ae974..1b189d5d1 100644 --- a/src/components/status.tsx +++ b/src/components/status.tsx @@ -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 = (props) => { {(quote || actualStatus.card || actualStatus.media_attachments.size > 0) && ( = ({ status }) => { - +
diff --git a/src/features/chats/components/chat-message.tsx b/src/features/chats/components/chat-message.tsx index 22204041d..5d5c26190 100644 --- a/src/features/chats/components/chat-message.tsx +++ b/src/features/chats/components/chat-message.tsx @@ -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 /> diff --git a/src/features/compose/components/reply-indicator.tsx b/src/features/compose/components/reply-indicator.tsx index 5570ec7cc..7d12ae738 100644 --- a/src/features/compose/components/reply-indicator.tsx +++ b/src/features/compose/components/reply-indicator.tsx @@ -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 = ({ 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 && ( { 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, }; }; diff --git a/src/features/event/event-information.tsx b/src/features/event/event-information.tsx index 0b01c27dd..9d64356a4 100644 --- a/src/features/event/event-information.tsx +++ b/src/features/event/event-information.tsx @@ -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 = ({ params }) => { )} diff --git a/src/features/report/components/status-check-box.tsx b/src/features/report/components/status-check-box.tsx index 149a5b352..29c63ee3f 100644 --- a/src/features/report/components/status-check-box.tsx +++ b/src/features/report/components/status-check-box.tsx @@ -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 = ({ id, disabled }) => { } else { media = ( {}} diff --git a/src/features/scheduled-statuses/components/scheduled-status.tsx b/src/features/scheduled-statuses/components/scheduled-status.tsx index ea45ef9aa..137f85ce8 100644 --- a/src/features/scheduled-statuses/components/scheduled-status.tsx +++ b/src/features/scheduled-statuses/components/scheduled-status.tsx @@ -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 = ({ statusId, ...other }) => {status.media_attachments.size > 0 && ( )} diff --git a/src/features/status/components/detailed-status.tsx b/src/features/status/components/detailed-status.tsx index 39bf23c7f..2f0c82053 100644 --- a/src/features/status/components/detailed-status.tsx +++ b/src/features/status/components/detailed-status.tsx @@ -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 = ({ {(withMedia && (quote || actualStatus.card || actualStatus.media_attachments.size > 0)) && ( diff --git a/src/features/ui/components/modals/actions-modal.tsx b/src/features/ui/components/modals/actions-modal.tsx index 6641298b6..9aacd9b3e 100644 --- a/src/features/ui/components/modals/actions-modal.tsx +++ b/src/features/ui/components/modals/actions-modal.tsx @@ -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; diff --git a/src/features/ui/components/modals/boost-modal.tsx b/src/features/ui/components/modals/boost-modal.tsx index 425e8580a..afe841a3c 100644 --- a/src/features/ui/components/modals/boost-modal.tsx +++ b/src/features/ui/components/modals/boost-modal.tsx @@ -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 = ({ status, onReblog, onClose }) => { confirmationText={intl.formatMessage(buttonText)} > - + {/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */} diff --git a/src/features/ui/components/modals/compare-history-modal.tsx b/src/features/ui/components/modals/compare-history-modal.tsx index 313de9291..133359516 100644 --- a/src/features/ui/components/modals/compare-history-modal.tsx +++ b/src/features/ui/components/modals/compare-history-modal.tsx @@ -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 = ({ onClose, statusId )} {version.media_attachments.size > 0 && ( - + )} diff --git a/src/features/ui/components/modals/media-modal.tsx b/src/features/ui/components/modals/media-modal.tsx index 2faa1de1d..59c3a6763 100644 --- a/src/features/ui/components/modals/media-modal.tsx +++ b/src/features/ui/components/modals/media-modal.tsx @@ -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; - status?: Status; + media: readonly Attachment[]; + status?: EntityTypes[Entities.STATUSES]; index: number; time?: number; onClose(): void; @@ -74,8 +74,7 @@ const MediaModal: React.FC = (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(!!status); const [next, setNext] = useState(); @@ -83,11 +82,11 @@ const MediaModal: React.FC = (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 = (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 = (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 && ( @@ -151,7 +150,7 @@ const MediaModal: React.FC = (props) => { return (