Delete Bundle component, use Suspense and React.lazy
This commit is contained in:
@@ -10,7 +10,6 @@ import { initReport, ReportableEntities } from 'soapbox/actions/reports';
|
||||
import DropdownMenu from 'soapbox/components/dropdown-menu';
|
||||
import { HStack, Icon, Stack, Text } from 'soapbox/components/ui';
|
||||
import emojify from 'soapbox/features/emoji';
|
||||
import Bundle from 'soapbox/features/ui/components/bundle';
|
||||
import { MediaGallery } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector, useFeatures } from 'soapbox/hooks';
|
||||
import { ChatKeys, IChat, useChatActions } from 'soapbox/queries/chats';
|
||||
@@ -22,7 +21,6 @@ import ChatMessageReaction from './chat-message-reaction';
|
||||
import ChatMessageReactionWrapper from './chat-message-reaction-wrapper/chat-message-reaction-wrapper';
|
||||
|
||||
import type { Menu as IMenu } from 'soapbox/components/dropdown-menu';
|
||||
import type { IMediaGallery } from 'soapbox/components/media-gallery';
|
||||
import type { ChatMessage as ChatMessageEntity } from 'soapbox/types/entities';
|
||||
|
||||
const messages = defineMessages({
|
||||
@@ -111,19 +109,15 @@ const ChatMessage = (props: IChatMessage) => {
|
||||
if (!chatMessage.media_attachments.size) return null;
|
||||
|
||||
return (
|
||||
<Bundle fetchComponent={MediaGallery}>
|
||||
{(Component: React.FC<IMediaGallery>) => (
|
||||
<Component
|
||||
className={clsx({
|
||||
'rounded-br-sm': isMyMessage && content,
|
||||
'rounded-bl-sm': !isMyMessage && content,
|
||||
})}
|
||||
media={chatMessage.media_attachments}
|
||||
onOpenMedia={onOpenMedia}
|
||||
visible
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
<MediaGallery
|
||||
className={clsx({
|
||||
'rounded-br-sm': isMyMessage && content,
|
||||
'rounded-bl-sm': !isMyMessage && content,
|
||||
})}
|
||||
media={chatMessage.media_attachments}
|
||||
onOpenMedia={onOpenMedia}
|
||||
visible
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -17,14 +17,12 @@ import AutosuggestInput, { AutoSuggestion } from 'soapbox/components/autosuggest
|
||||
import AutosuggestTextarea from 'soapbox/components/autosuggest-textarea';
|
||||
import { Button, HStack, Stack } from 'soapbox/components/ui';
|
||||
import EmojiPickerDropdown from 'soapbox/features/emoji/containers/emoji-picker-dropdown-container';
|
||||
import Bundle from 'soapbox/features/ui/components/bundle';
|
||||
import { ComposeEditor } from 'soapbox/features/ui/util/async-components';
|
||||
import { ComposeEditor, ScheduleForm } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector, useCompose, useDraggedFiles, useFeatures, useInstance, usePrevious } from 'soapbox/hooks';
|
||||
import { isMobile } from 'soapbox/is-mobile';
|
||||
|
||||
import QuotedStatusContainer from '../containers/quoted-status-container';
|
||||
import ReplyIndicatorContainer from '../containers/reply-indicator-container';
|
||||
import ScheduleFormContainer from '../containers/schedule-form-container';
|
||||
import UploadButtonContainer from '../containers/upload-button-container';
|
||||
import WarningContainer from '../containers/warning-container';
|
||||
import { $createEmojiNode } from '../editor/nodes/emoji-node';
|
||||
@@ -260,7 +258,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
ref={spoilerTextRef}
|
||||
/>
|
||||
|
||||
<ScheduleFormContainer composeId={id} />
|
||||
<ScheduleForm composeId={id} />
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -313,23 +311,19 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
|
||||
{!shouldCondense && !event && !group && <ReplyMentions composeId={id} />}
|
||||
|
||||
<div>
|
||||
<Bundle fetchComponent={ComposeEditor}>
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
ref={editorRef}
|
||||
className='mt-2'
|
||||
composeId={id}
|
||||
condensed={condensed}
|
||||
eventDiscussion={!!event}
|
||||
autoFocus={shouldAutoFocus}
|
||||
hasPoll={hasPoll}
|
||||
handleSubmit={handleSubmit}
|
||||
onChange={setText}
|
||||
onFocus={handleComposeFocus}
|
||||
onPaste={onPaste}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
<ComposeEditor
|
||||
ref={editorRef}
|
||||
className='mt-2'
|
||||
composeId={id}
|
||||
condensed={condensed}
|
||||
eventDiscussion={!!event}
|
||||
autoFocus={shouldAutoFocus}
|
||||
hasPoll={hasPoll}
|
||||
handleSubmit={handleSubmit}
|
||||
onChange={setText}
|
||||
onFocus={handleComposeFocus}
|
||||
onPaste={onPaste}
|
||||
/>
|
||||
{composeModifiers}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||
import { setSchedule, removeSchedule } from 'soapbox/actions/compose';
|
||||
import IconButton from 'soapbox/components/icon-button';
|
||||
import { HStack, Stack, Text } from 'soapbox/components/ui';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||
import { DatePicker } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
@@ -55,22 +54,20 @@ const ScheduleForm: React.FC<IScheduleForm> = ({ composeId }) => {
|
||||
<FormattedMessage id='datepicker.hint' defaultMessage='Scheduled to post at…' />
|
||||
</Text>
|
||||
<HStack space={2} alignItems='center'>
|
||||
<BundleContainer fetchComponent={DatePicker}>
|
||||
{Component => (<Component
|
||||
selected={scheduledAt}
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
onChange={onSchedule}
|
||||
placeholderText={intl.formatMessage(messages.schedule)}
|
||||
filterDate={isCurrentOrFutureDate}
|
||||
filterTime={isFiveMinutesFromNow}
|
||||
className={clsx({
|
||||
'has-error': !isFiveMinutesFromNow(scheduledAt),
|
||||
})}
|
||||
/>)}
|
||||
</BundleContainer>
|
||||
<DatePicker
|
||||
selected={scheduledAt}
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
onChange={onSchedule}
|
||||
placeholderText={intl.formatMessage(messages.schedule)}
|
||||
filterDate={isCurrentOrFutureDate}
|
||||
filterTime={isFiveMinutesFromNow}
|
||||
className={clsx({
|
||||
'has-error': !isFiveMinutesFromNow(scheduledAt),
|
||||
})}
|
||||
/>
|
||||
<IconButton
|
||||
iconClassName='h-4 w-4'
|
||||
className='bg-transparent text-gray-400 hover:text-gray-600'
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||
import { ScheduleForm } from 'soapbox/features/ui/util/async-components';
|
||||
|
||||
import type { IScheduleForm } from '../components/schedule-form';
|
||||
|
||||
const ScheduleFormContainer: React.FC<IScheduleForm> = (props) => (
|
||||
<BundleContainer fetchComponent={ScheduleForm}>
|
||||
{Component => <Component {...props} />}
|
||||
</BundleContainer>
|
||||
);
|
||||
|
||||
export default ScheduleFormContainer;
|
||||
@@ -6,7 +6,6 @@ import StatusContent from 'soapbox/components/status-content';
|
||||
import { Toggle } from 'soapbox/components/ui';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
import Bundle from '../../ui/components/bundle';
|
||||
import { MediaGallery, Video, Audio } from '../../ui/util/async-components';
|
||||
|
||||
interface IStatusCheckBox {
|
||||
@@ -35,22 +34,16 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
|
||||
|
||||
if (video) {
|
||||
media = (
|
||||
<Bundle fetchComponent={Video}>
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
preview={video.preview_url}
|
||||
blurhash={video.blurhash}
|
||||
src={video.url}
|
||||
alt={video.description}
|
||||
aspectRatio={video.meta.getIn(['original', 'aspect'])}
|
||||
width={239}
|
||||
height={110}
|
||||
inline
|
||||
sensitive={status.sensitive}
|
||||
onOpenVideo={noop}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
<Video
|
||||
preview={video.preview_url}
|
||||
blurhash={video.blurhash}
|
||||
src={video.url}
|
||||
alt={video.description}
|
||||
aspectRatio={video.meta.getIn(['original', 'aspect']) as number | undefined}
|
||||
width={239}
|
||||
height={110}
|
||||
inline
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else if (status.media_attachments.get(0)?.type === 'audio') {
|
||||
@@ -58,24 +51,20 @@ const StatusCheckBox: React.FC<IStatusCheckBox> = ({ id, disabled }) => {
|
||||
|
||||
if (audio) {
|
||||
media = (
|
||||
<Bundle fetchComponent={Audio}>
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
src={audio.url}
|
||||
alt={audio.description}
|
||||
inline
|
||||
sensitive={status.sensitive}
|
||||
onOpenAudio={noop}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
<Audio
|
||||
src={audio.url}
|
||||
alt={audio.description}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
media = (
|
||||
<Bundle fetchComponent={MediaGallery}>
|
||||
{(Component: any) => <Component media={status.media_attachments} sensitive={status.sensitive} height={110} onOpenMedia={noop} />}
|
||||
</Bundle>
|
||||
<MediaGallery
|
||||
media={status.media_attachments}
|
||||
sensitive={status.sensitive}
|
||||
height={110}
|
||||
onOpenMedia={noop}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import React, { Suspense } from 'react';
|
||||
|
||||
export interface IBundle<T extends React.ComponentType<any>> {
|
||||
fetchComponent: React.LazyExoticComponent<T>;
|
||||
loading?: React.ComponentType;
|
||||
error?: React.ComponentType<{ onRetry: (props?: IBundle<T>) => void }>;
|
||||
children: (component: React.LazyExoticComponent<T>) => React.ReactNode;
|
||||
renderDelay?: number;
|
||||
onFetch?: () => void;
|
||||
onFetchSuccess?: () => void;
|
||||
onFetchFail?: (error: any) => void;
|
||||
}
|
||||
|
||||
/** Fetches and renders an async component. */
|
||||
function Bundle<T extends React.ComponentType<any>>({ fetchComponent, loading: Loading, children }: IBundle<T>) {
|
||||
return (
|
||||
<Suspense fallback={Loading ? <Loading /> : null}>
|
||||
{children(fetchComponent)}
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
export default Bundle;
|
||||
@@ -2,8 +2,6 @@ import React from 'react';
|
||||
|
||||
import { Spinner } from 'soapbox/components/ui';
|
||||
|
||||
// Keep the markup in sync with <BundleModalError />
|
||||
// (make sure they have the same dimensions)
|
||||
const ModalLoading = () => (
|
||||
<div className='modal-root__modal error-modal'>
|
||||
<div className='error-modal__body'>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { Suspense } from 'react';
|
||||
|
||||
import Base from 'soapbox/components/modal-root';
|
||||
import {
|
||||
@@ -38,8 +38,6 @@ import {
|
||||
VideoModal,
|
||||
} from 'soapbox/features/ui/util/async-components';
|
||||
|
||||
import BundleContainer from '../containers/bundle-container';
|
||||
|
||||
import ModalLoading from './modal-loading';
|
||||
|
||||
/* eslint sort-keys: "error" */
|
||||
@@ -102,7 +100,7 @@ export default class ModalRoot extends React.PureComponent<IModalRoot> {
|
||||
}
|
||||
}
|
||||
|
||||
renderLoading = (modalId: string) => () => {
|
||||
renderLoading = (modalId: string) => {
|
||||
return !['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].includes(modalId) ? <ModalLoading /> : null;
|
||||
};
|
||||
|
||||
@@ -113,14 +111,14 @@ export default class ModalRoot extends React.PureComponent<IModalRoot> {
|
||||
|
||||
render() {
|
||||
const { type, props } = this.props;
|
||||
const visible = !!type;
|
||||
const Component = type ? MODAL_COMPONENTS[type] : null;
|
||||
|
||||
return (
|
||||
<Base onClose={this.onClickClose} type={type}>
|
||||
{visible && (
|
||||
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} renderDelay={200}>
|
||||
{(SpecificComponent) => <SpecificComponent {...props} onClose={this.onClickClose} />}
|
||||
</BundleContainer>
|
||||
{(Component && !!type) && (
|
||||
<Suspense fallback={this.renderLoading(type)}>
|
||||
<Component {...props} onClose={this.onClickClose} />
|
||||
</Suspense>
|
||||
)}
|
||||
</Base>
|
||||
);
|
||||
|
||||
@@ -24,12 +24,13 @@ import { checkEventComposeContent } from 'soapbox/components/modal-root';
|
||||
import { Button, Form, FormGroup, HStack, Icon, IconButton, Input, Modal, Spinner, Stack, Tabs, Text, Toggle } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
import { isCurrentOrFutureDate } from 'soapbox/features/compose/components/schedule-form';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||
import { ComposeEditor, DatePicker } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
import UploadButton from './upload-button';
|
||||
|
||||
import type { LexicalEditor } from 'lexical';
|
||||
|
||||
const messages = defineMessages({
|
||||
eventNamePlaceholder: { id: 'compose_event.fields.name_placeholder', defaultMessage: 'Name' },
|
||||
eventDescriptionPlaceholder: { id: 'compose_event.fields.description_placeholder', defaultMessage: 'Description' },
|
||||
@@ -94,7 +95,7 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const editorStateRef = useRef<string>(null);
|
||||
const editorRef = useRef<LexicalEditor>(null);
|
||||
|
||||
const [tab, setTab] = useState<'edit' | 'pending'>('edit');
|
||||
|
||||
@@ -167,7 +168,7 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
dispatch(changeEditEventDescription(editorStateRef.current!));
|
||||
dispatch(changeEditEventDescription(editorRef.current!));
|
||||
dispatch(submitEvent());
|
||||
};
|
||||
|
||||
@@ -235,18 +236,14 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='compose_event.fields.description_label' defaultMessage='Event description' />}
|
||||
>
|
||||
<BundleContainer fetchComponent={ComposeEditor}>
|
||||
{(Component: any) => (
|
||||
<Component
|
||||
ref={editorStateRef}
|
||||
className='block w-full rounded-md border border-gray-400 bg-white px-3 py-2 text-base text-gray-900 ring-1 placeholder:text-gray-600 focus-within:border-primary-500 focus-within:ring-primary-500 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus-within:border-primary-500 dark:focus-within:ring-primary-500 sm:text-sm'
|
||||
placeholderClassName='pt-2'
|
||||
composeId='compose-event-modal'
|
||||
placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
)}
|
||||
</BundleContainer>
|
||||
<ComposeEditor
|
||||
ref={editorRef}
|
||||
className='block w-full rounded-md border border-gray-400 bg-white px-3 py-2 text-base text-gray-900 ring-1 placeholder:text-gray-600 focus-within:border-primary-500 focus-within:ring-primary-500 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-100 dark:ring-gray-800 dark:placeholder:text-gray-600 dark:focus-within:border-primary-500 dark:focus-within:ring-primary-500 sm:text-sm'
|
||||
placeholderClassName='pt-2'
|
||||
composeId='compose-event-modal'
|
||||
placeholder={intl.formatMessage(messages.eventDescriptionPlaceholder)}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='compose_event.fields.location_label' defaultMessage='Event location' />}
|
||||
@@ -260,18 +257,16 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='compose_event.fields.start_time_label' defaultMessage='Event start date' />}
|
||||
>
|
||||
<BundleContainer fetchComponent={DatePicker}>
|
||||
{Component => (<Component
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.eventStartTimePlaceholder)}
|
||||
filterDate={isCurrentOrFutureDate}
|
||||
selected={startTime}
|
||||
onChange={onChangeStartTime}
|
||||
/>)}
|
||||
</BundleContainer>
|
||||
<DatePicker
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.eventStartTimePlaceholder)}
|
||||
filterDate={isCurrentOrFutureDate}
|
||||
selected={startTime}
|
||||
onChange={onChangeStartTime}
|
||||
/>
|
||||
</FormGroup>
|
||||
<HStack alignItems='center' space={2}>
|
||||
<Toggle
|
||||
@@ -286,18 +281,16 @@ const ComposeEventModal: React.FC<IComposeEventModal> = ({ onClose }) => {
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='compose_event.fields.end_time_label' defaultMessage='Event end date' />}
|
||||
>
|
||||
<BundleContainer fetchComponent={DatePicker}>
|
||||
{Component => (<Component
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.eventEndTimePlaceholder)}
|
||||
filterDate={isCurrentOrFutureDate}
|
||||
selected={endTime}
|
||||
onChange={onChangeEndTime}
|
||||
/>)}
|
||||
</BundleContainer>
|
||||
<DatePicker
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.eventEndTimePlaceholder)}
|
||||
filterDate={isCurrentOrFutureDate}
|
||||
selected={endTime}
|
||||
onChange={onChangeEndTime}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
{!id && (
|
||||
|
||||
@@ -4,7 +4,6 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||
import { changeAnnouncementAllDay, changeAnnouncementContent, changeAnnouncementEndTime, changeAnnouncementStartTime, handleCreateAnnouncement } from 'soapbox/actions/admin';
|
||||
import { closeModal } from 'soapbox/actions/modals';
|
||||
import { Form, FormGroup, HStack, Modal, Stack, Text, Textarea, Toggle } from 'soapbox/components/ui';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||
import { DatePicker } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
@@ -67,34 +66,30 @@ const EditAnnouncementModal: React.FC<IEditAnnouncementModal> = ({ onClose }) =>
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='admin.edit_announcement.fields.start_time_label' defaultMessage='Start date' />}
|
||||
>
|
||||
<BundleContainer fetchComponent={DatePicker}>
|
||||
{Component => (<Component
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.announcementStartTimePlaceholder)}
|
||||
selected={startTime}
|
||||
onChange={onChangeStartTime}
|
||||
isClearable
|
||||
/>)}
|
||||
</BundleContainer>
|
||||
<DatePicker
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.announcementStartTimePlaceholder)}
|
||||
selected={startTime}
|
||||
onChange={onChangeStartTime}
|
||||
isClearable
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
labelText={<FormattedMessage id='admin.edit_announcement.fields.end_time_label' defaultMessage='End date' />}
|
||||
>
|
||||
<BundleContainer fetchComponent={DatePicker}>
|
||||
{Component => (<Component
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.announcementEndTimePlaceholder)}
|
||||
selected={endTime}
|
||||
onChange={onChangeEndTime}
|
||||
isClearable
|
||||
/>)}
|
||||
</BundleContainer>
|
||||
<DatePicker
|
||||
showTimeSelect
|
||||
dateFormat='MMMM d, yyyy h:mm aa'
|
||||
timeIntervals={15}
|
||||
wrapperClassName='react-datepicker-wrapper'
|
||||
placeholderText={intl.formatMessage(messages.announcementEndTimePlaceholder)}
|
||||
selected={endTime}
|
||||
onChange={onChangeEndTime}
|
||||
isClearable
|
||||
/>
|
||||
</FormGroup>
|
||||
<HStack alignItems='center' space={2}>
|
||||
<Toggle
|
||||
|
||||
@@ -5,7 +5,6 @@ import { FormattedMessage } from 'react-intl';
|
||||
import { fetchPinnedAccounts } from 'soapbox/actions/accounts';
|
||||
import { Widget } from 'soapbox/components/ui';
|
||||
import AccountContainer from 'soapbox/containers/account-container';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||
import { WhoToFollowPanel } from 'soapbox/features/ui/util/async-components';
|
||||
import { useAppDispatch, useAppSelector } from 'soapbox/hooks';
|
||||
|
||||
@@ -26,9 +25,7 @@ const PinnedAccountsPanel: React.FC<IPinnedAccountsPanel> = ({ account, limit })
|
||||
|
||||
if (pinned.isEmpty()) {
|
||||
return (
|
||||
<BundleContainer fetchComponent={WhoToFollowPanel}>
|
||||
{Component => <Component limit={limit} />}
|
||||
</BundleContainer>
|
||||
<WhoToFollowPanel limit={limit} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { defineMessages, useIntl, FormatDateOptions } from 'react-intl';
|
||||
|
||||
import Markup from 'soapbox/components/markup';
|
||||
import { HStack, Icon } from 'soapbox/components/ui';
|
||||
import BundleContainer from 'soapbox/features/ui/containers/bundle-container';
|
||||
import { CryptoAddress } from 'soapbox/features/ui/util/async-components';
|
||||
|
||||
import type { Account } from 'soapbox/schemas';
|
||||
@@ -35,14 +34,10 @@ const ProfileField: React.FC<IProfileField> = ({ field }) => {
|
||||
|
||||
if (isTicker(field.name)) {
|
||||
return (
|
||||
<BundleContainer fetchComponent={CryptoAddress}>
|
||||
{Component => (
|
||||
<Component
|
||||
ticker={getTicker(field.name).toLowerCase()}
|
||||
address={field.value_plain}
|
||||
/>
|
||||
)}
|
||||
</BundleContainer>
|
||||
<CryptoAddress
|
||||
ticker={getTicker(field.name).toLowerCase()}
|
||||
address={field.value_plain}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
import Bundle from '../components/bundle';
|
||||
|
||||
export default Bundle;
|
||||
@@ -41,7 +41,6 @@ import { isStandalone } from 'soapbox/utils/state';
|
||||
import BackgroundShapes from './components/background-shapes';
|
||||
import FloatingActionButton from './components/floating-action-button';
|
||||
import Navbar from './components/navbar';
|
||||
import BundleContainer from './containers/bundle-container';
|
||||
import {
|
||||
Status,
|
||||
CommunityTimeline,
|
||||
@@ -501,29 +500,18 @@ const UI: React.FC<IUI> = ({ children }) => {
|
||||
)}
|
||||
|
||||
{me && (
|
||||
<BundleContainer fetchComponent={SidebarMenu}>
|
||||
{Component => <Component />}
|
||||
</BundleContainer>
|
||||
<SidebarMenu />
|
||||
)}
|
||||
|
||||
{me && features.chats && (
|
||||
<BundleContainer fetchComponent={ChatWidget}>
|
||||
{Component => (
|
||||
<div className='hidden xl:block'>
|
||||
<Component />
|
||||
</div>
|
||||
)}
|
||||
</BundleContainer>
|
||||
<div className='hidden xl:block'>
|
||||
<ChatWidget />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<ThumbNavigation />
|
||||
|
||||
<BundleContainer fetchComponent={ProfileHoverCard}>
|
||||
{Component => <Component />}
|
||||
</BundleContainer>
|
||||
|
||||
<BundleContainer fetchComponent={StatusHoverCard}>
|
||||
{Component => <Component />}
|
||||
</BundleContainer>
|
||||
<ProfileHoverCard />
|
||||
<StatusHoverCard />
|
||||
</div>
|
||||
</div>
|
||||
</GlobalHotkeys>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { Suspense } from 'react';
|
||||
import { Redirect, Route, useHistory, RouteProps, RouteComponentProps, match as MatchType } from 'react-router-dom';
|
||||
|
||||
import { Layout } from 'soapbox/components/ui';
|
||||
@@ -7,7 +7,6 @@ import { useOwnAccount, useSettings } from 'soapbox/hooks';
|
||||
import ColumnForbidden from '../components/column-forbidden';
|
||||
import ColumnLoading from '../components/column-loading';
|
||||
import ColumnsArea from '../components/columns-area';
|
||||
import BundleContainer from '../containers/bundle-container';
|
||||
|
||||
type PageProps = {
|
||||
params?: MatchType['params'];
|
||||
@@ -28,7 +27,7 @@ interface IWrappedRoute extends RouteProps {
|
||||
}
|
||||
|
||||
const WrappedRoute: React.FC<IWrappedRoute> = ({
|
||||
component,
|
||||
component: Component,
|
||||
page: Page,
|
||||
content,
|
||||
componentParams = {},
|
||||
@@ -47,32 +46,24 @@ const WrappedRoute: React.FC<IWrappedRoute> = ({
|
||||
const renderComponent = ({ match }: RouteComponentProps) => {
|
||||
if (Page) {
|
||||
return (
|
||||
<BundleContainer fetchComponent={component} loading={renderLoading}>
|
||||
{Component =>
|
||||
(
|
||||
<Page params={match.params} layout={layout} {...componentParams}>
|
||||
<Component params={match.params} {...componentParams}>
|
||||
{content}
|
||||
</Component>
|
||||
</Page>
|
||||
)
|
||||
}
|
||||
</BundleContainer>
|
||||
<Suspense fallback={renderLoading()}>
|
||||
<Page params={match.params} layout={layout} {...componentParams}>
|
||||
<Component params={match.params} {...componentParams}>
|
||||
{content}
|
||||
</Component>
|
||||
</Page>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<BundleContainer fetchComponent={component} loading={renderLoading}>
|
||||
{Component =>
|
||||
(
|
||||
<ColumnsArea layout={layout}>
|
||||
<Component params={match.params} {...componentParams}>
|
||||
{content}
|
||||
</Component>
|
||||
</ColumnsArea>
|
||||
)
|
||||
}
|
||||
</BundleContainer>
|
||||
<Suspense fallback={renderLoading()}>
|
||||
<ColumnsArea layout={layout}>
|
||||
<Component params={match.params} {...componentParams}>
|
||||
{content}
|
||||
</Component>
|
||||
</ColumnsArea>
|
||||
</Suspense>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user