Cleanup
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
c61dcddd81
commit
6465423ccb
|
@ -19,12 +19,13 @@ const messages = defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IEventPreview {
|
interface IEventPreview {
|
||||||
status: StatusEntity,
|
status: StatusEntity
|
||||||
className?: string,
|
className?: string
|
||||||
hideAction?: boolean;
|
hideAction?: boolean
|
||||||
|
floatingAction?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const EventPreview: React.FC<IEventPreview> = ({ status, className, hideAction }) => {
|
const EventPreview: React.FC<IEventPreview> = ({ status, className, hideAction, floatingAction = true }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
|
|
||||||
const me = useAppSelector((state) => state.me);
|
const me = useAppSelector((state) => state.me);
|
||||||
|
@ -32,26 +33,37 @@ const EventPreview: React.FC<IEventPreview> = ({ status, className, hideAction }
|
||||||
const account = status.account as AccountEntity;
|
const account = status.account as AccountEntity;
|
||||||
const event = status.event!;
|
const event = status.event!;
|
||||||
|
|
||||||
const banner = status.media_attachments?.find(({ description }) => description === 'Banner');
|
const banner = event.banner;
|
||||||
|
|
||||||
return (
|
const action = !hideAction && (account.id === me ? (
|
||||||
<div className={classNames('w-full rounded-lg bg-gray-100 dark:bg-primary-800 relative overflow-hidden', className)}>
|
|
||||||
<div className='absolute top-28 right-3'>
|
|
||||||
{!hideAction && (account.id === me ? (
|
|
||||||
<Button
|
<Button
|
||||||
size='sm'
|
size='sm'
|
||||||
theme='secondary'
|
theme={floatingAction ? 'secondary' : 'primary'}
|
||||||
to={`/@${account.acct}/events/${status.id}`}
|
to={`/@${account.acct}/events/${status.id}`}
|
||||||
>
|
>
|
||||||
<FormattedMessage id='event.manage' defaultMessage='Manage' />
|
<FormattedMessage id='event.manage' defaultMessage='Manage' />
|
||||||
</Button>
|
</Button>
|
||||||
) : <EventActionButton status={status} />)}
|
) : (
|
||||||
|
<EventActionButton
|
||||||
|
status={status}
|
||||||
|
theme={floatingAction ? 'secondary' : 'primary'}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('w-full rounded-lg bg-gray-100 dark:bg-primary-800 relative overflow-hidden', className)}>
|
||||||
|
<div className='absolute top-28 right-3'>
|
||||||
|
{floatingAction && action}
|
||||||
</div>
|
</div>
|
||||||
<div className='bg-primary-200 dark:bg-gray-600 h-40'>
|
<div className='bg-primary-200 dark:bg-gray-600 h-40'>
|
||||||
{banner && <img className='h-full w-full object-cover' src={banner.url} alt={intl.formatMessage(messages.bannerHeader)} />}
|
{banner && <img className='h-full w-full object-cover' src={banner.url} alt={intl.formatMessage(messages.bannerHeader)} />}
|
||||||
</div>
|
</div>
|
||||||
<Stack className='p-2.5' space={2}>
|
<Stack className='p-2.5' space={2}>
|
||||||
<Text weight='semibold'>{event.name}</Text>
|
<HStack space={2} alignItems='center' justifyContent='between'>
|
||||||
|
<Text weight='semibold' truncate>{event.name}</Text>
|
||||||
|
|
||||||
|
{!floatingAction && action}
|
||||||
|
</HStack>
|
||||||
|
|
||||||
<div className='flex gap-y-1 gap-x-2 flex-wrap text-gray-700 dark:text-gray-600'>
|
<div className='flex gap-y-1 gap-x-2 flex-wrap text-gray-700 dark:text-gray-600'>
|
||||||
<HStack alignItems='center' space={2}>
|
<HStack alignItems='center' space={2}>
|
||||||
|
|
|
@ -24,8 +24,6 @@ interface IStatusMedia {
|
||||||
showMedia?: boolean,
|
showMedia?: boolean,
|
||||||
/** Callback when visibility is toggled (eg clicked through NSFW). */
|
/** Callback when visibility is toggled (eg clicked through NSFW). */
|
||||||
onToggleVisibility?: () => void,
|
onToggleVisibility?: () => void,
|
||||||
/** Whether or not to hide image describer as 'Banner' */
|
|
||||||
excludeBanner?: boolean,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Render media attachments for a status. */
|
/** Render media attachments for a status. */
|
||||||
|
@ -35,7 +33,6 @@ const StatusMedia: React.FC<IStatusMedia> = ({
|
||||||
onClick,
|
onClick,
|
||||||
showMedia = true,
|
showMedia = true,
|
||||||
onToggleVisibility = () => { },
|
onToggleVisibility = () => { },
|
||||||
excludeBanner = false,
|
|
||||||
}) => {
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
|
@ -43,10 +40,8 @@ const StatusMedia: React.FC<IStatusMedia> = ({
|
||||||
|
|
||||||
const [mediaWrapperWidth, setMediaWrapperWidth] = useState<number | undefined>(undefined);
|
const [mediaWrapperWidth, setMediaWrapperWidth] = useState<number | undefined>(undefined);
|
||||||
|
|
||||||
const mediaAttachments = excludeBanner ? status.media_attachments.filter(({ description, pleroma }) => description !== 'Banner' && pleroma.get('mime_type') !== 'text/html') : status.media_attachments;
|
const size = status.media_attachments.size;
|
||||||
|
const firstAttachment = status.media_attachments.first();
|
||||||
const size = mediaAttachments.size;
|
|
||||||
const firstAttachment = mediaAttachments.first();
|
|
||||||
|
|
||||||
let media: JSX.Element | null = null;
|
let media: JSX.Element | null = null;
|
||||||
|
|
||||||
|
@ -76,7 +71,7 @@ const StatusMedia: React.FC<IStatusMedia> = ({
|
||||||
if (muted) {
|
if (muted) {
|
||||||
media = (
|
media = (
|
||||||
<AttachmentThumbs
|
<AttachmentThumbs
|
||||||
media={mediaAttachments}
|
media={status.media_attachments}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
sensitive={status.sensitive}
|
sensitive={status.sensitive}
|
||||||
/>
|
/>
|
||||||
|
@ -147,7 +142,7 @@ const StatusMedia: React.FC<IStatusMedia> = ({
|
||||||
<Bundle fetchComponent={MediaGallery} loading={renderLoadingMediaGallery}>
|
<Bundle fetchComponent={MediaGallery} loading={renderLoadingMediaGallery}>
|
||||||
{(Component: any) => (
|
{(Component: any) => (
|
||||||
<Component
|
<Component
|
||||||
media={mediaAttachments}
|
media={status.media_attachments}
|
||||||
sensitive={status.sensitive}
|
sensitive={status.sensitive}
|
||||||
height={285}
|
height={285}
|
||||||
onOpenMedia={openMedia}
|
onOpenMedia={openMedia}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { openModal } from 'soapbox/actions/modals';
|
||||||
import { Button } from 'soapbox/components/ui';
|
import { Button } from 'soapbox/components/ui';
|
||||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import type { ButtonThemes } from 'soapbox/components/ui/button/useButtonStyles';
|
||||||
import type { Status as StatusEntity } from 'soapbox/types/entities';
|
import type { Status as StatusEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
@ -14,10 +15,11 @@ const messages = defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IEventAction {
|
interface IEventAction {
|
||||||
status: StatusEntity,
|
status: StatusEntity
|
||||||
|
theme?: ButtonThemes
|
||||||
}
|
}
|
||||||
|
|
||||||
const EventActionButton: React.FC<IEventAction> = ({ status }) => {
|
const EventActionButton: React.FC<IEventAction> = ({ status, theme = 'secondary' }) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
@ -86,7 +88,7 @@ const EventActionButton: React.FC<IEventAction> = ({ status }) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
size='sm'
|
size='sm'
|
||||||
theme='secondary'
|
theme={theme}
|
||||||
icon={buttonIcon}
|
icon={buttonIcon}
|
||||||
onClick={buttonAction}
|
onClick={buttonAction}
|
||||||
disabled={buttonDisabled}
|
disabled={buttonDisabled}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
import { Link, useHistory } from 'react-router-dom';
|
import { Link, useHistory } from 'react-router-dom';
|
||||||
|
@ -86,15 +87,14 @@ const EventHeader: React.FC<IEventHeader> = ({ status }) => {
|
||||||
|
|
||||||
const account = status.account as AccountEntity;
|
const account = status.account as AccountEntity;
|
||||||
const event = status.event;
|
const event = status.event;
|
||||||
const banner = status.media_attachments?.find(({ description }) => description === 'Banner');
|
const banner = event.banner;
|
||||||
|
|
||||||
const username = account.username;
|
const username = account.username;
|
||||||
|
|
||||||
const handleHeaderClick: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
|
const handleHeaderClick: React.MouseEventHandler<HTMLAnchorElement> = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
||||||
const index = status.media_attachments!.findIndex(({ description }) => description === 'Banner');
|
dispatch(openModal('MEDIA', { media: ImmutableList([event.banner]) }));
|
||||||
dispatch(openModal('MEDIA', { media: status.media_attachments, index }));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExportClick = () => {
|
const handleExportClick = () => {
|
||||||
|
|
|
@ -4,8 +4,11 @@ import { FormattedDate, FormattedMessage } from 'react-intl';
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
import { fetchStatus } from 'soapbox/actions/statuses';
|
import { fetchStatus } from 'soapbox/actions/statuses';
|
||||||
import MissingIndicator from 'soapbox/components/missing-indicator';
|
import MissingIndicator from 'soapbox/components/missing-indicator';
|
||||||
|
import StatusContent from 'soapbox/components/status-content';
|
||||||
import StatusMedia from 'soapbox/components/status-media';
|
import StatusMedia from 'soapbox/components/status-media';
|
||||||
|
import TranslateButton from 'soapbox/components/translate-button';
|
||||||
import { HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
import { HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||||
|
import QuotedStatus from 'soapbox/features/status/containers/quoted-status-container';
|
||||||
import { useAppDispatch, useAppSelector, useSettings } from 'soapbox/hooks';
|
import { useAppDispatch, useAppSelector, useSettings } from 'soapbox/hooks';
|
||||||
import { makeGetStatus } from 'soapbox/selectors';
|
import { makeGetStatus } from 'soapbox/selectors';
|
||||||
import { defaultMediaVisibility } from 'soapbox/utils/status';
|
import { defaultMediaVisibility } from 'soapbox/utils/status';
|
||||||
|
@ -107,10 +110,7 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
const renderLinks = useCallback(() => {
|
const renderLinks = useCallback(() => {
|
||||||
const links = status?.media_attachments.filter(({ pleroma }) => pleroma.get('mime_type') === 'text/html');
|
if (!status.event?.links.size) return null;
|
||||||
|
|
||||||
if (!links?.size) return null;
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack space={1}>
|
<Stack space={1}>
|
||||||
|
@ -118,11 +118,11 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
|
||||||
<FormattedMessage id='event.website' defaultMessage='External links' />
|
<FormattedMessage id='event.website' defaultMessage='External links' />
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{links.map(link => (
|
{status.event.links.map(link => (
|
||||||
<HStack space={2} alignItems='center'>
|
<HStack space={2} alignItems='center'>
|
||||||
<Icon src={require('@tabler/icons/link.svg')} />
|
<Icon src={require('@tabler/icons/link.svg')} />
|
||||||
<a href={link.remote_url || link.url} className='text-primary-600 dark:text-accent-blue hover:underline' target='_blank'>
|
<a href={link.url} className='text-primary-600 dark:text-accent-blue hover:underline' target='_blank'>
|
||||||
{(link.remote_url || link.url).replace(/^https?:\/\//, '')}
|
{link.url.replace(/^https?:\/\//, '')}
|
||||||
</a>
|
</a>
|
||||||
</HStack>
|
</HStack>
|
||||||
))}
|
))}
|
||||||
|
@ -143,21 +143,23 @@ const EventInformation: React.FC<IEventInformation> = ({ params }) => {
|
||||||
<Text size='xl' weight='bold'>
|
<Text size='xl' weight='bold'>
|
||||||
<FormattedMessage id='event.description' defaultMessage='Description' />
|
<FormattedMessage id='event.description' defaultMessage='Description' />
|
||||||
</Text>
|
</Text>
|
||||||
<Text
|
|
||||||
className='break-words status__content'
|
<StatusContent status={status} collapsable={false} translatable />
|
||||||
size='sm'
|
|
||||||
dangerouslySetInnerHTML={{ __html: status.contentHtml }}
|
<TranslateButton status={status} />
|
||||||
/>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<StatusMedia
|
<StatusMedia
|
||||||
status={status}
|
status={status}
|
||||||
excludeBanner
|
|
||||||
showMedia={showMedia}
|
showMedia={showMedia}
|
||||||
onToggleVisibility={handleToggleMediaVisibility}
|
onToggleVisibility={handleToggleMediaVisibility}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{status.quote && status.pleroma.get('quote_visible', true) && (
|
||||||
|
<QuotedStatus statusId={status.quote as string} />
|
||||||
|
)}
|
||||||
|
|
||||||
{renderEventLocation()}
|
{renderEventLocation()}
|
||||||
|
|
||||||
{renderEventDate()}
|
{renderEventDate()}
|
||||||
|
|
|
@ -22,7 +22,7 @@ const Event = ({ id }: { id: string }) => {
|
||||||
className='w-full px-1'
|
className='w-full px-1'
|
||||||
to={`/@${status.getIn(['account', 'acct'])}/events/${status.id}`}
|
to={`/@${status.getIn(['account', 'acct'])}/events/${status.id}`}
|
||||||
>
|
>
|
||||||
<EventPreview status={status} />
|
<EventPreview status={status} floatingAction={false} />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -56,7 +56,6 @@ const EventCarousel: React.FC<IEventCarousel> = ({ statusIds, isLoading, emptyMe
|
||||||
{index !== 0 && (
|
{index !== 0 && (
|
||||||
<div className='z-10 absolute left-3 top-1/2 -mt-4'>
|
<div className='z-10 absolute left-3 top-1/2 -mt-4'>
|
||||||
<button
|
<button
|
||||||
data-testid='prev-page'
|
|
||||||
onClick={() => handleChangeIndex(index - 1)}
|
onClick={() => handleChangeIndex(index - 1)}
|
||||||
className='bg-white/50 dark:bg-gray-900/50 backdrop-blur rounded-full h-8 w-8 flex items-center justify-center'
|
className='bg-white/50 dark:bg-gray-900/50 backdrop-blur rounded-full h-8 w-8 flex items-center justify-center'
|
||||||
>
|
>
|
||||||
|
@ -70,7 +69,6 @@ const EventCarousel: React.FC<IEventCarousel> = ({ statusIds, isLoading, emptyMe
|
||||||
{index !== statusIds.size - 1 && (
|
{index !== statusIds.size - 1 && (
|
||||||
<div className='z-10 absolute right-3 top-1/2 -mt-4'>
|
<div className='z-10 absolute right-3 top-1/2 -mt-4'>
|
||||||
<button
|
<button
|
||||||
data-testid='next-page'
|
|
||||||
onClick={() => handleChangeIndex(index + 1)}
|
onClick={() => handleChangeIndex(index + 1)}
|
||||||
className='bg-white/50 dark:bg-gray-900/50 backdrop-blur rounded-full h-8 w-8 flex items-center justify-center'
|
className='bg-white/50 dark:bg-gray-900/50 backdrop-blur rounded-full h-8 w-8 flex items-center justify-center'
|
||||||
>
|
>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
import { fetchJoinedEvents, fetchRecentEvents } from 'soapbox/actions/events';
|
import { fetchJoinedEvents, fetchRecentEvents } from 'soapbox/actions/events';
|
||||||
|
|
|
@ -10,9 +10,7 @@ const PlaceholderEventPreview = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full rounded-lg bg-gray-100 dark:bg-primary-800 relative overflow-hidden animate-pulse text-primary-50 dark:text-primary-800'>
|
<div className='w-full rounded-lg bg-gray-100 dark:bg-primary-800 relative overflow-hidden animate-pulse text-primary-50 dark:text-primary-800'>
|
||||||
<div className='bg-primary-200 dark:bg-gray-600 h-40'>
|
<div className='bg-primary-200 dark:bg-gray-600 h-40' />
|
||||||
{/* <img className='h-full w-full object-cover' src={banner.url} alt={intl.formatMessage(messages.bannerHeader)} />} */}
|
|
||||||
</div>
|
|
||||||
<Stack className='p-2.5' space={2}>
|
<Stack className='p-2.5' space={2}>
|
||||||
<Text weight='semibold'>{generateText(eventNameLength)}</Text>
|
<Text weight='semibold'>{generateText(eventNameLength)}</Text>
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ export const EventRecord = ImmutableRecord({
|
||||||
participants_count: 0,
|
participants_count: 0,
|
||||||
location: null as ImmutableMap<string, any> | null,
|
location: null as ImmutableMap<string, any> | null,
|
||||||
join_state: null as EventJoinState | null,
|
join_state: null as EventJoinState | null,
|
||||||
|
banner: null as Attachment | null,
|
||||||
|
links: ImmutableList<Attachment>(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// https://docs.joinmastodon.org/entities/status/
|
// https://docs.joinmastodon.org/entities/status/
|
||||||
|
@ -174,9 +176,27 @@ const fixSensitivity = (status: ImmutableMap<string, any>) => {
|
||||||
// Normalize event
|
// Normalize event
|
||||||
const normalizeEvent = (status: ImmutableMap<string, any>) => {
|
const normalizeEvent = (status: ImmutableMap<string, any>) => {
|
||||||
if (status.getIn(['pleroma', 'event'])) {
|
if (status.getIn(['pleroma', 'event'])) {
|
||||||
return status.set('event', EventRecord(status.getIn(['pleroma', 'event']) as ImmutableMap<string, any>));
|
const firstAttachment = status.get('media_attachments').first();
|
||||||
} else {
|
let banner = null;
|
||||||
return status.set('event', null);
|
let mediaAttachments = status.get('media_attachments');
|
||||||
|
|
||||||
|
if (firstAttachment && firstAttachment.description === 'Banner' && firstAttachment.type === 'image') {
|
||||||
|
banner = normalizeAttachment(firstAttachment);
|
||||||
|
mediaAttachments = mediaAttachments.shift();
|
||||||
|
}
|
||||||
|
|
||||||
|
const links = mediaAttachments.filter((attachment: Attachment) => attachment.pleroma.get('mime_type') === 'text/html');
|
||||||
|
mediaAttachments = mediaAttachments.filter((attachment: Attachment) => attachment.pleroma.get('mime_type') !== 'text/html');
|
||||||
|
|
||||||
|
const event = EventRecord(
|
||||||
|
(status.getIn(['pleroma', 'event']) as ImmutableMap<string, any>)
|
||||||
|
.set('banner', banner)
|
||||||
|
.set('links', links),
|
||||||
|
);
|
||||||
|
|
||||||
|
status
|
||||||
|
.set('event', event)
|
||||||
|
.set('media_attachments', mediaAttachments);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import { normalizeAttachment, normalizeLocation } from 'soapbox/normalizers';
|
||||||
import type {
|
import type {
|
||||||
Attachment as AttachmentEntity,
|
Attachment as AttachmentEntity,
|
||||||
Location as LocationEntity,
|
Location as LocationEntity,
|
||||||
Status as StatusEntity,
|
|
||||||
} from 'soapbox/types/entities';
|
} from 'soapbox/types/entities';
|
||||||
|
|
||||||
export const ReducerRecord = ImmutableRecord({
|
export const ReducerRecord = ImmutableRecord({
|
||||||
|
@ -90,11 +89,10 @@ export default function compose_event(state = ReducerRecord(), action: AnyAction
|
||||||
return ReducerRecord({
|
return ReducerRecord({
|
||||||
name: action.status.event.name,
|
name: action.status.event.name,
|
||||||
status: action.text,
|
status: action.text,
|
||||||
// location: null as LocationEntity | null,
|
|
||||||
start_time: new Date(action.status.event.start_time),
|
start_time: new Date(action.status.event.start_time),
|
||||||
end_time: action.status.event.end_time ? new Date(action.status.event.end_time) : null,
|
end_time: action.status.event.end_time ? new Date(action.status.event.end_time) : null,
|
||||||
approval_required: action.status.event.join_mode !== 'free',
|
approval_required: action.status.event.join_mode !== 'free',
|
||||||
banner: (action.status as StatusEntity).media_attachments.find(({ description }) => description === 'Banner') || null,
|
banner: action.status.event.banner || null,
|
||||||
location: action.location ? normalizeLocation(action.location) : null,
|
location: action.location ? normalizeLocation(action.location) : null,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
is_uploading: false,
|
is_uploading: false,
|
||||||
|
|
Loading…
Reference in New Issue