kopia lustrzana https://gitlab.com/soapbox-pub/soapbox
Merge branch 'refactor-immutable-to-plain-object' into 'main'
refactor: remove immutable, convert to use plain javascript object See merge request soapbox-pub/soapbox!3294merge-requests/3305/head
commit
95f134785d
|
@ -1,14 +1,20 @@
|
|||
import { EntityTypes, Entities } from 'soapbox/entity-store/entities.ts';
|
||||
import { Entities } from 'soapbox/entity-store/entities.ts';
|
||||
import { useEntities } from 'soapbox/entity-store/hooks/index.ts';
|
||||
import { useApi } from 'soapbox/hooks/useApi.ts';
|
||||
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
||||
import { statusSchema } from 'soapbox/schemas/status.ts';
|
||||
import { Status as StatusEntity, statusSchema } from 'soapbox/schemas/index.ts';
|
||||
|
||||
/**
|
||||
* Get all the statuses the user has bookmarked.
|
||||
* https://docs.joinmastodon.org/methods/bookmarks/#get
|
||||
* GET /api/v1/bookmarks
|
||||
* TODO: add 'limit'
|
||||
*/
|
||||
function useBookmarks() {
|
||||
const api = useApi();
|
||||
const features = useFeatures();
|
||||
|
||||
const { entities, ...result } = useEntities<EntityTypes[Entities.STATUSES]>(
|
||||
const { entities, ...result } = useEntities<StatusEntity>(
|
||||
[Entities.STATUSES, 'bookmarks'],
|
||||
() => api.get('/api/v1/bookmarks'),
|
||||
{ enabled: features.bookmarks, schema: statusSchema },
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -11,11 +11,10 @@ import IconButton from 'soapbox/components/ui/icon-button.tsx';
|
|||
import Portal from 'soapbox/components/ui/portal.tsx';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { userTouching } from 'soapbox/is-mobile.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.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?: StatusEntity;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -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!] || 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}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -7,7 +7,6 @@ import Button from 'soapbox/components/ui/button.tsx';
|
|||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import Stack from 'soapbox/components/ui/stack.tsx';
|
||||
import Text from 'soapbox/components/ui/text.tsx';
|
||||
import { EntityTypes, Entities } from 'soapbox/entity-store/entities.ts';
|
||||
import PureEventActionButton from 'soapbox/features/event/components/pure-event-action-button.tsx';
|
||||
import PureEventDate from 'soapbox/features/event/components/pure-event-date.tsx';
|
||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||
|
@ -15,6 +14,7 @@ import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
|||
import Icon from './icon.tsx';
|
||||
import VerificationBadge from './verification-badge.tsx';
|
||||
|
||||
import type { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
const messages = defineMessages({
|
||||
eventBanner: { id: 'event.banner', defaultMessage: 'Event banner' },
|
||||
|
@ -23,7 +23,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IPureEventPreview {
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
className?: string;
|
||||
hideAction?: boolean;
|
||||
floatingAction?: boolean;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { useState, useRef, useLayoutEffect, useMemo, memo } from 'react';
|
|||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { isOnlyEmoji as _isOnlyEmoji } from 'soapbox/utils/only-emoji.ts';
|
||||
import { getTextDirection } from 'soapbox/utils/rtl.ts';
|
||||
|
||||
|
@ -28,7 +28,7 @@ const ReadMoreButton: React.FC<IReadMoreButton> = ({ onClick }) => (
|
|||
);
|
||||
|
||||
interface IPureStatusContent {
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
onClick?: () => void;
|
||||
collapsable?: boolean;
|
||||
translatable?: boolean;
|
||||
|
|
|
@ -6,11 +6,11 @@ import { FormattedMessage } from 'react-intl';
|
|||
import LoadGap from 'soapbox/components/load-gap.tsx';
|
||||
import PureStatus from 'soapbox/components/pure-status.tsx';
|
||||
import ScrollableList from 'soapbox/components/scrollable-list.tsx';
|
||||
import { EntityTypes, Entities } from 'soapbox/entity-store/entities.ts';
|
||||
import FeedSuggestions from 'soapbox/features/feed-suggestions/feed-suggestions.tsx';
|
||||
import PlaceholderStatus from 'soapbox/features/placeholder/components/placeholder-status.tsx';
|
||||
import PendingStatus from 'soapbox/features/ui/components/pending-status.tsx';
|
||||
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
import type { VirtuosoHandle } from 'react-virtuoso';
|
||||
import type { IScrollableList } from 'soapbox/components/scrollable-list.tsx';
|
||||
|
@ -19,11 +19,11 @@ interface IPureStatusList extends Omit<IScrollableList, 'onLoadMore' | 'children
|
|||
/** Unique key to preserve the scroll position when navigating back. */
|
||||
scrollKey: string;
|
||||
/** List of statuses to display. */
|
||||
statuses: readonly EntityTypes[Entities.STATUSES][];
|
||||
statuses: readonly StatusEntity[];
|
||||
/** Last _unfiltered_ status ID (maxId) for pagination. */
|
||||
lastStatusId?: string;
|
||||
/** Pinned statuses to show at the top of the feed. */
|
||||
featuredStatuses?: readonly EntityTypes[Entities.STATUSES][];
|
||||
featuredStatuses?: readonly StatusEntity[];
|
||||
/** Pagination callback when the end of the list is reached. */
|
||||
onLoadMore?: (lastStatusId: string) => void;
|
||||
/** Whether the data is currently being fetched. */
|
||||
|
@ -124,7 +124,7 @@ const PureStatusList: React.FC<IPureStatusList> = ({
|
|||
);
|
||||
};
|
||||
|
||||
const renderStatus = (status: EntityTypes[Entities.STATUSES]) => {
|
||||
const renderStatus = (status: StatusEntity) => {
|
||||
return (
|
||||
<PureStatus
|
||||
status={status}
|
||||
|
|
|
@ -4,13 +4,12 @@ import { Link } from 'react-router-dom';
|
|||
import { openModal } from 'soapbox/actions/modals.ts';
|
||||
import HoverRefWrapper from 'soapbox/components/hover-ref-wrapper.tsx';
|
||||
import HoverStatusWrapper from 'soapbox/components/hover-status-wrapper.tsx';
|
||||
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { shortenNostr } from 'soapbox/utils/nostr.ts';
|
||||
|
||||
|
||||
interface IPureStatusReplyMentions {
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
hoverable?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
@ -19,18 +18,18 @@ 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 { 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 as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
import { emojifyText } from 'soapbox/utils/emojify.tsx';
|
||||
import { defaultMediaVisibility, textForScreenReader, getActualStatus } from 'soapbox/utils/status.ts';
|
||||
|
||||
|
@ -49,7 +48,7 @@ const messages = defineMessages({
|
|||
export interface IPureStatus {
|
||||
id?: string;
|
||||
avatarSize?: number;
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
onClick?: () => void;
|
||||
muted?: boolean;
|
||||
hidden?: boolean;
|
||||
|
@ -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 LegacyStatus; // 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}
|
||||
|
|
|
@ -5,14 +5,14 @@ import { translateStatus, undoStatusTranslation } from 'soapbox/actions/statuses
|
|||
import Button from 'soapbox/components/ui/button.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 { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||
import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
||||
import { useInstance } from 'soapbox/hooks/useInstance.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
interface IPureTranslateButton {
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
}
|
||||
|
||||
const PureTranslateButton: React.FC<IPureTranslateButton> = ({ status }) => {
|
||||
|
|
|
@ -8,6 +8,7 @@ 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 { useSettings } from 'soapbox/hooks/useSettings.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { defaultMediaVisibility } from 'soapbox/utils/status.ts';
|
||||
|
||||
import EventPreview from './event-preview.tsx';
|
||||
|
@ -17,7 +18,7 @@ import StatusContent from './status-content.tsx';
|
|||
import StatusReplyMentions from './status-reply-mentions.tsx';
|
||||
import SensitiveContentOverlay from './statuses/sensitive-content-overlay.tsx';
|
||||
|
||||
import type { Status as StatusEntity } from 'soapbox/types/entities.ts';
|
||||
import type { Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
const messages = defineMessages({
|
||||
cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
|
||||
|
@ -25,7 +26,7 @@ const messages = defineMessages({
|
|||
|
||||
interface IQuotedStatus {
|
||||
/** The quoted status entity. */
|
||||
status?: StatusEntity;
|
||||
status?: LegacyStatus;
|
||||
/** Callback when cancelled (during compose). */
|
||||
onCancel?: Function;
|
||||
/** Whether the status is shown in the post composer. */
|
||||
|
@ -138,7 +139,7 @@ const QuotedStatus: React.FC<IQuotedStatus> = ({ status, onCancel, compose }) =>
|
|||
|
||||
{status.media_attachments.size > 0 && (
|
||||
<StatusMedia
|
||||
status={status}
|
||||
status={status.toJS() as StatusEntity}
|
||||
muted={compose}
|
||||
showMedia={showMedia}
|
||||
onToggleVisibility={handleToggleMediaVisibility}
|
||||
|
|
|
@ -55,13 +55,14 @@ import { useFeatures } from 'soapbox/hooks/useFeatures.ts';
|
|||
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
|
||||
import { useSettings } from 'soapbox/hooks/useSettings.ts';
|
||||
import { GroupRoles } from 'soapbox/schemas/group-member.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import toast from 'soapbox/toast.tsx';
|
||||
import copy from 'soapbox/utils/copy.ts';
|
||||
|
||||
import GroupPopover from './groups/popover/group-popover.tsx';
|
||||
|
||||
import type { Menu } from 'soapbox/components/dropdown-menu/index.ts';
|
||||
import type { Group, Status } from 'soapbox/types/entities.ts';
|
||||
import type { Group, Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
const messages = defineMessages({
|
||||
adminAccount: { id: 'status.admin_account', defaultMessage: 'Moderate @{name}' },
|
||||
|
@ -140,7 +141,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IStatusActionBar {
|
||||
status: Status;
|
||||
status: LegacyStatus;
|
||||
expandable?: boolean;
|
||||
space?: 'sm' | 'md' | 'lg';
|
||||
statusActionButtonTheme?: 'default' | 'inverse';
|
||||
|
@ -826,7 +827,7 @@ const StatusActionBar: React.FC<IStatusActionBar> = ({
|
|||
/>
|
||||
)}
|
||||
|
||||
<DropdownMenu items={menu} status={status}>
|
||||
<DropdownMenu items={menu} status={status.toJS() as StatusEntity}>
|
||||
<StatusActionButton
|
||||
title={intl.formatMessage(messages.more)}
|
||||
icon={dotsIcon}
|
||||
|
|
|
@ -8,13 +8,11 @@ import { GroupLinkPreview } from 'soapbox/features/groups/components/group-link-
|
|||
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 { Status as StatusEntity, Attachment } from 'soapbox/schemas/index.ts';
|
||||
|
||||
interface IStatusMedia {
|
||||
/** Status entity to render media for. */
|
||||
status: Status;
|
||||
status: StatusEntity;
|
||||
/** Whether to display compact media. */
|
||||
muted?: boolean;
|
||||
/** Callback when compact media is clicked. */
|
||||
|
@ -35,8 +33,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 +50,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 +70,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 +88,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>
|
||||
|
|
|
@ -20,6 +20,7 @@ import QuotedStatus from 'soapbox/features/status/containers/quoted-status-conta
|
|||
import { HotKeys } from 'soapbox/features/ui/components/hotkeys.tsx';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useSettings } from 'soapbox/hooks/useSettings.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { emojifyText } from 'soapbox/utils/emojify.tsx';
|
||||
import { defaultMediaVisibility, textForScreenReader, getActualStatus } from 'soapbox/utils/status.ts';
|
||||
|
||||
|
@ -32,7 +33,7 @@ import SensitiveContentOverlay from './statuses/sensitive-content-overlay.tsx';
|
|||
import StatusInfo from './statuses/status-info.tsx';
|
||||
import Tombstone from './tombstone.tsx';
|
||||
|
||||
import type { Status as StatusEntity } from 'soapbox/types/entities.ts';
|
||||
import type { Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
// Defined in components/scrollable-list
|
||||
export type ScrollPosition = { height: number; top: number };
|
||||
|
@ -44,7 +45,7 @@ const messages = defineMessages({
|
|||
export interface IStatus {
|
||||
id?: string;
|
||||
avatarSize?: number;
|
||||
status: StatusEntity;
|
||||
status: LegacyStatus;
|
||||
onClick?: () => void;
|
||||
muted?: boolean;
|
||||
hidden?: boolean;
|
||||
|
@ -150,7 +151,7 @@ const Status: React.FC<IStatus> = (props) => {
|
|||
if (firstAttachment.type === 'video') {
|
||||
dispatch(openModal('VIDEO', { status, media: firstAttachment, time: 0 }));
|
||||
} else {
|
||||
dispatch(openModal('MEDIA', { status, media: status.media_attachments, index: 0 }));
|
||||
dispatch(openModal('MEDIA', { status: status.toJS(), media: status.media_attachments.toJS(), index: 0 }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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 StatusEntity}
|
||||
muted={muted}
|
||||
onClick={handleClick}
|
||||
showMedia={showMedia}
|
||||
|
|
|
@ -13,15 +13,14 @@ import DropdownMenu from 'soapbox/components/dropdown-menu/index.ts';
|
|||
import Button from 'soapbox/components/ui/button.tsx';
|
||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import Text from 'soapbox/components/ui/text.tsx';
|
||||
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
|
||||
import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { useOwnAccount } from 'soapbox/hooks/useOwnAccount.ts';
|
||||
import { useSettings } from 'soapbox/hooks/useSettings.ts';
|
||||
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { emojifyText } from 'soapbox/utils/emojify.tsx';
|
||||
import { defaultMediaVisibility } from 'soapbox/utils/status.ts';
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
|
||||
|
@ -37,7 +36,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IPureSensitiveContentOverlay {
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
onToggleVisibility?(): void;
|
||||
visible?: boolean;
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ const Upload: React.FC<IUpload> = ({
|
|||
};
|
||||
|
||||
const handleOpenModal = () => {
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(media), index: 0 }));
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(media).toJS(), index: 0 }));
|
||||
};
|
||||
|
||||
const active = hovered || focused;
|
||||
|
|
|
@ -74,7 +74,7 @@ const AccountGallery = () => {
|
|||
const media = (attachment.status as Status).media_attachments;
|
||||
const index = media.findIndex((x) => x.id === attachment.id);
|
||||
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status }));
|
||||
dispatch(openModal('MEDIA', { media: media.toJS(), index, status: attachment?.status?.toJS() ?? attachment.status }));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -266,7 +266,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
type: 'image',
|
||||
url: account.avatar,
|
||||
});
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(avatar), index: 0 }));
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(avatar).toJS(), index: 0 }));
|
||||
};
|
||||
|
||||
const handleAvatarClick: React.MouseEventHandler = (e) => {
|
||||
|
@ -281,7 +281,7 @@ const Header: React.FC<IHeader> = ({ account }) => {
|
|||
type: 'image',
|
||||
url: account.header,
|
||||
});
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(header), index: 0 }));
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(header).toJS(), index: 0 }));
|
||||
};
|
||||
|
||||
const handleHeaderClick: React.MouseEventHandler = (e) => {
|
||||
|
|
|
@ -10,8 +10,9 @@ 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 { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
import type { AdminReport, Status } from 'soapbox/types/entities.ts';
|
||||
import type { AdminReport, Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
const messages = defineMessages({
|
||||
viewStatus: { id: 'admin.reports.actions.view_status', defaultMessage: 'View post' },
|
||||
|
@ -19,7 +20,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IReportStatus {
|
||||
status: Status;
|
||||
status: LegacyStatus;
|
||||
report?: AdminReport;
|
||||
}
|
||||
|
||||
|
@ -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 StatusEntity} />
|
||||
</Stack>
|
||||
|
||||
<div className='flex-none'>
|
||||
|
|
|
@ -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
|
||||
/>
|
||||
|
|
|
@ -22,7 +22,7 @@ const ChatUpload: React.FC<IChatUpload> = ({ attachment, onDelete }) => {
|
|||
const clickable = attachment.type !== 'unknown';
|
||||
|
||||
const handleOpenModal = () => {
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(attachment), index: 0 }));
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(attachment).toJS(), index: 0 }));
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -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 { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { getTextDirection } from 'soapbox/utils/rtl.ts';
|
||||
|
||||
import type { Status } from 'soapbox/types/entities.ts';
|
||||
|
||||
interface IReplyIndicator {
|
||||
className?: string;
|
||||
status?: Status;
|
||||
status?: StatusEntity;
|
||||
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}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { cancelReplyCompose } from 'soapbox/actions/compose.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { makeGetStatus } from 'soapbox/selectors/index.ts';
|
||||
|
||||
import ReplyIndicator from '../components/reply-indicator.tsx';
|
||||
|
||||
import type { AppDispatch, RootState } from 'soapbox/store.ts';
|
||||
import type { Status } from 'soapbox/types/entities.ts';
|
||||
import type { Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getStatus = makeGetStatus();
|
||||
|
@ -16,7 +18,7 @@ const makeMapStateToProps = () => {
|
|||
const editing = !!state.compose.get(composeId)?.id;
|
||||
|
||||
return {
|
||||
status: getStatus(state, { id: statusId }) as Status,
|
||||
status: (getStatus(state, { id: statusId }) as LegacyStatus)?.toJS() as StatusEntity,
|
||||
hideActions: editing,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -128,7 +128,7 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
|||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList([event.banner]) }));
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList([event.banner]).toJS() }));
|
||||
};
|
||||
|
||||
const handleExportClick = () => {
|
||||
|
|
|
@ -5,9 +5,9 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|||
import { joinEvent, leaveEvent } from 'soapbox/actions/events.ts';
|
||||
import { openModal } from 'soapbox/actions/modals.ts';
|
||||
import Button from 'soapbox/components/ui/button.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 { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
import type { ButtonThemes } from 'soapbox/components/ui/useButtonStyles.ts';
|
||||
|
||||
|
@ -17,7 +17,7 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IPureEventAction {
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
theme?: ButtonThemes;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,11 +3,10 @@ import { FormattedDate } from 'react-intl';
|
|||
|
||||
import Icon from 'soapbox/components/icon.tsx';
|
||||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import { Entities, EntityTypes } from 'soapbox/entity-store/entities.ts';
|
||||
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
interface IPureEventDate {
|
||||
status: EntityTypes[Entities.STATUSES];
|
||||
status: StatusEntity;
|
||||
}
|
||||
|
||||
const PureEventDate: React.FC<IPureEventDate> = ({ status }) => {
|
||||
|
|
|
@ -20,10 +20,11 @@ import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts';
|
|||
import { useAppSelector } from 'soapbox/hooks/useAppSelector.ts';
|
||||
import { useSettings } from 'soapbox/hooks/useSettings.ts';
|
||||
import { useSoapboxConfig } from 'soapbox/hooks/useSoapboxConfig.ts';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { makeGetStatus } from 'soapbox/selectors/index.ts';
|
||||
import { defaultMediaVisibility } from 'soapbox/utils/status.ts';
|
||||
|
||||
import type { Status as StatusEntity } from 'soapbox/types/entities.ts';
|
||||
import type { Status as StatusLegacy } from 'soapbox/types/entities.ts';
|
||||
|
||||
type RouteParams = { statusId: string };
|
||||
|
||||
|
@ -35,7 +36,7 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
|
|||
const dispatch = useAppDispatch();
|
||||
const getStatus = useCallback(makeGetStatus(), []);
|
||||
|
||||
const status = useAppSelector(state => getStatus(state, { id: params.statusId })) as StatusEntity;
|
||||
const status = useAppSelector(state => getStatus(state, { id: params.statusId })) as StatusLegacy;
|
||||
|
||||
const { tileServer } = useSoapboxConfig();
|
||||
const { displayMedia } = useSettings();
|
||||
|
@ -208,7 +209,7 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
|
|||
)}
|
||||
|
||||
<StatusMedia
|
||||
status={status}
|
||||
status={status.toJS() as StatusEntity}
|
||||
showMedia={showMedia}
|
||||
onToggleVisibility={handleToggleMediaVisibility}
|
||||
/>
|
||||
|
|
|
@ -63,7 +63,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
|||
type: 'image',
|
||||
url: group.avatar,
|
||||
});
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(avatar), index: 0 }));
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(avatar).toJS(), index: 0 }));
|
||||
};
|
||||
|
||||
const handleAvatarClick: React.MouseEventHandler = (e) => {
|
||||
|
@ -78,7 +78,7 @@ const GroupHeader: React.FC<IGroupHeader> = ({ group }) => {
|
|||
type: 'image',
|
||||
url: group.header,
|
||||
});
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(header), index: 0 }));
|
||||
dispatch(openModal('MEDIA', { media: ImmutableList.of(header).toJS(), index: 0 }));
|
||||
};
|
||||
|
||||
const handleHeaderClick: React.MouseEventHandler = (e) => {
|
||||
|
|
|
@ -43,7 +43,7 @@ const GroupGallery: React.FC<IGroupGallery> = (props) => {
|
|||
const media = (attachment.status as Status).media_attachments;
|
||||
const index = media.findIndex((x) => x.id === attachment.id);
|
||||
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status }));
|
||||
dispatch(openModal('MEDIA', { media: media.toJS(), index, status: attachment?.status?.toJS() ?? attachment.status }));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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={() => {}}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -17,17 +17,18 @@ 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 QuotedStatus from 'soapbox/features/status/containers/quoted-status-container.tsx';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
import { getActualStatus } from 'soapbox/utils/status.ts';
|
||||
|
||||
import StatusInteractionBar from './status-interaction-bar.tsx';
|
||||
|
||||
import type { Group, Status as StatusEntity } from 'soapbox/types/entities.ts';
|
||||
import type { Group, Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
interface IDetailedStatus {
|
||||
status: StatusEntity;
|
||||
status: LegacyStatus;
|
||||
showMedia?: boolean;
|
||||
withMedia?: boolean;
|
||||
onOpenCompareHistoryModal: (status: StatusEntity) => void;
|
||||
onOpenCompareHistoryModal: (status: LegacyStatus) => void;
|
||||
onToggleMediaVisibility: () => void;
|
||||
}
|
||||
|
||||
|
@ -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 StatusEntity}
|
||||
showMedia={showMedia}
|
||||
onToggleVisibility={onToggleMediaVisibility}
|
||||
/>
|
||||
|
|
|
@ -179,7 +179,7 @@ const Thread = (props: IThread) => {
|
|||
if (media.size === 1 && firstAttachment.type === 'video') {
|
||||
dispatch(openModal('VIDEO', { media: firstAttachment, status: status }));
|
||||
} else {
|
||||
dispatch(openModal('MEDIA', { media, index: 0, status: status }));
|
||||
dispatch(openModal('MEDIA', { media: media.toJS(), index: 0, status: status.toJS() }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -36,7 +36,7 @@ const GroupMediaPanel: React.FC<IGroupMediaPanel> = ({ group }) => {
|
|||
const media = attachment.getIn(['status', 'media_attachments']) as ImmutableList<Attachment>;
|
||||
const index = media.findIndex(x => x.id === attachment.id);
|
||||
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status, account: attachment.account }));
|
||||
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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ import { spring } from 'react-motion';
|
|||
import HStack from 'soapbox/components/ui/hstack.tsx';
|
||||
import SvgIcon from 'soapbox/components/ui/svg-icon.tsx';
|
||||
import ReplyIndicator from 'soapbox/features/compose/components/reply-indicator.tsx';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
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;
|
||||
|
|
|
@ -6,8 +6,9 @@ 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 ReplyIndicator from 'soapbox/features/compose/components/reply-indicator.tsx';
|
||||
import { Status as StatusEntity } from 'soapbox/schemas/index.ts';
|
||||
|
||||
import type { Status as StatusEntity } from 'soapbox/types/entities.ts';
|
||||
import type { Status as LegacyStatus } from 'soapbox/types/entities.ts';
|
||||
|
||||
const messages = defineMessages({
|
||||
cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Un-repost' },
|
||||
|
@ -15,8 +16,8 @@ const messages = defineMessages({
|
|||
});
|
||||
|
||||
interface IBoostModal {
|
||||
status: StatusEntity;
|
||||
onReblog: (status: StatusEntity) => void;
|
||||
status: LegacyStatus;
|
||||
onReblog: (status: LegacyStatus) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
|
@ -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 StatusEntity} hideActions />
|
||||
|
||||
<Text>
|
||||
{/* eslint-disable-next-line formatjs/no-literal-string-in-jsx */}
|
||||
|
|
|
@ -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'>
|
||||
|
|
|
@ -24,15 +24,14 @@ import PlaceholderStatus from 'soapbox/features/placeholder/components/placehold
|
|||
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 { Status as StatusEntity, Attachment } from 'soapbox/schemas/index.ts';
|
||||
import { Status as LegacyStatus } 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 +54,8 @@ const containerStyle: React.CSSProperties = {
|
|||
};
|
||||
|
||||
interface IMediaModal {
|
||||
media: ImmutableList<Attachment>;
|
||||
status?: Status;
|
||||
media: readonly Attachment[];
|
||||
status?: StatusEntity;
|
||||
index: number;
|
||||
time?: number;
|
||||
onClose(): void;
|
||||
|
@ -74,8 +73,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 +81,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 +105,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 +124,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 +149,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 +167,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 +191,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
|
|||
}
|
||||
|
||||
return null;
|
||||
}).toArray();
|
||||
});
|
||||
|
||||
const handleLoadMore = useCallback(debounce(() => {
|
||||
if (next && status) {
|
||||
|
@ -344,7 +342,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 LegacyStatus}
|
||||
space='md'
|
||||
statusActionButtonTheme='inverse'
|
||||
/>
|
||||
|
@ -362,7 +360,7 @@ const MediaModal: React.FC<IMediaModal> = (props) => {
|
|||
}
|
||||
>
|
||||
<Thread
|
||||
status={actualStatus}
|
||||
status={normalizeStatus(actualStatus) as LegacyStatus}
|
||||
withMedia={false}
|
||||
useWindowScroll={false}
|
||||
itemClassName='px-4'
|
||||
|
|
|
@ -21,6 +21,7 @@ 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';
|
||||
|
@ -91,7 +92,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}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -34,7 +34,7 @@ const ProfileMediaPanel: React.FC<IProfileMediaPanel> = ({ account }) => {
|
|||
const media = attachment.getIn(['status', 'media_attachments']) as ImmutableList<Attachment>;
|
||||
const index = media.findIndex(x => x.id === attachment.id);
|
||||
|
||||
dispatch(openModal('MEDIA', { media, index, status: attachment.status }));
|
||||
dispatch(openModal('MEDIA', { media: media.toJS(), index, status: attachment?.status?.toJS() ?? attachment.status }));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -43,6 +43,9 @@ type Tag = ReturnType<typeof TagRecord>;
|
|||
|
||||
type Account = SchemaAccount & LegacyMap;
|
||||
|
||||
/**
|
||||
* @deprecated Use the Status from 'soapbox/schemas/index.ts'
|
||||
*/
|
||||
interface Status extends ReturnType<typeof StatusRecord> {
|
||||
// HACK: same as above
|
||||
quote: EmbeddedEntity<Status>;
|
||||
|
|
Ładowanie…
Reference in New Issue