Scheduled statuses: TypeScript, fix styles
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
7c3d0c821b
commit
73c4457f28
9 changed files with 187 additions and 245 deletions
|
@ -56,6 +56,7 @@ interface IAccount {
|
||||||
showProfileHoverCard?: boolean,
|
showProfileHoverCard?: boolean,
|
||||||
timestamp?: string | Date,
|
timestamp?: string | Date,
|
||||||
timestampUrl?: string,
|
timestampUrl?: string,
|
||||||
|
futureTimestamp?: boolean,
|
||||||
withDate?: boolean,
|
withDate?: boolean,
|
||||||
withRelationship?: boolean,
|
withRelationship?: boolean,
|
||||||
showEdit?: boolean,
|
showEdit?: boolean,
|
||||||
|
@ -75,6 +76,7 @@ const Account = ({
|
||||||
showProfileHoverCard = true,
|
showProfileHoverCard = true,
|
||||||
timestamp,
|
timestamp,
|
||||||
timestampUrl,
|
timestampUrl,
|
||||||
|
futureTimestamp = false,
|
||||||
withDate = false,
|
withDate = false,
|
||||||
withRelationship = true,
|
withRelationship = true,
|
||||||
showEdit = false,
|
showEdit = false,
|
||||||
|
@ -205,10 +207,10 @@ const Account = ({
|
||||||
|
|
||||||
{timestampUrl ? (
|
{timestampUrl ? (
|
||||||
<Link to={timestampUrl} className='hover:underline'>
|
<Link to={timestampUrl} className='hover:underline'>
|
||||||
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' />
|
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' futureDate={futureTimestamp} />
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' />
|
<RelativeTimestamp timestamp={timestamp} theme='muted' size='sm' className='whitespace-nowrap' futureDate={futureTimestamp} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import React from 'react';
|
||||||
const justifyContentOptions = {
|
const justifyContentOptions = {
|
||||||
between: 'justify-between',
|
between: 'justify-between',
|
||||||
center: 'justify-center',
|
center: 'justify-center',
|
||||||
|
start: 'justify-start',
|
||||||
|
end: 'justify-end',
|
||||||
};
|
};
|
||||||
|
|
||||||
const alignItemsOptions = {
|
const alignItemsOptions = {
|
||||||
|
@ -29,7 +31,7 @@ interface IHStack {
|
||||||
/** Extra class names on the <div> element. */
|
/** Extra class names on the <div> element. */
|
||||||
className?: string,
|
className?: string,
|
||||||
/** Horizontal alignment of children. */
|
/** Horizontal alignment of children. */
|
||||||
justifyContent?: 'between' | 'center',
|
justifyContent?: 'between' | 'center' | 'start' | 'end',
|
||||||
/** Size of the gap between elements. */
|
/** Size of the gap between elements. */
|
||||||
space?: 0.5 | 1 | 1.5 | 2 | 3 | 4 | 6,
|
space?: 0.5 | 1 | 1.5 | 2 | 3 | 4 | 6,
|
||||||
/** Whether to let the flexbox grow. */
|
/** Whether to let the flexbox grow. */
|
||||||
|
|
|
@ -3,11 +3,13 @@ import { Map as ImmutableMap } from 'immutable';
|
||||||
import { normalizeStatus } from 'soapbox/normalizers/status';
|
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||||
import { calculateStatus } from 'soapbox/reducers/statuses';
|
import { calculateStatus } from 'soapbox/reducers/statuses';
|
||||||
import { makeGetAccount } from 'soapbox/selectors';
|
import { makeGetAccount } from 'soapbox/selectors';
|
||||||
|
import { RootState } from 'soapbox/store';
|
||||||
|
|
||||||
export const buildStatus = (state, scheduledStatus) => {
|
export const buildStatus = (state: RootState, scheduledStatus: ImmutableMap<string, any>) => {
|
||||||
const getAccount = makeGetAccount();
|
const getAccount = makeGetAccount();
|
||||||
|
|
||||||
const me = state.get('me');
|
const me = state.me as string;
|
||||||
|
|
||||||
const params = scheduledStatus.get('params');
|
const params = scheduledStatus.get('params');
|
||||||
const account = getAccount(state, me);
|
const account = getAccount(state, me);
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
import classNames from 'classnames';
|
|
||||||
import React from 'react';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
|
||||||
|
|
||||||
import AttachmentThumbs from 'soapbox/components/attachment-thumbs';
|
|
||||||
import Avatar from 'soapbox/components/avatar';
|
|
||||||
import DisplayName from 'soapbox/components/display-name';
|
|
||||||
import RelativeTimestamp from 'soapbox/components/relative_timestamp';
|
|
||||||
import StatusContent from 'soapbox/components/status_content';
|
|
||||||
import StatusReplyMentions from 'soapbox/components/status_reply_mentions';
|
|
||||||
import PollPreview from 'soapbox/features/ui/components/poll_preview';
|
|
||||||
import { getDomain } from 'soapbox/utils/accounts';
|
|
||||||
|
|
||||||
import { buildStatus } from '../builder';
|
|
||||||
|
|
||||||
import ScheduledStatusActionBar from './scheduled_status_action_bar';
|
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => {
|
|
||||||
const scheduledStatus = state.getIn(['scheduled_statuses', props.statusId]);
|
|
||||||
return {
|
|
||||||
status: buildStatus(state, scheduledStatus),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
|
||||||
class ScheduledStatus extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { status, account, ...other } = this.props;
|
|
||||||
if (!status.get('account')) return null;
|
|
||||||
|
|
||||||
const statusUrl = `/scheduled_statuses/${status.get('id')}`;
|
|
||||||
const favicon = status.getIn(['account', 'pleroma', 'favicon']);
|
|
||||||
const domain = getDomain(status.get('account'));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='scheduled-status'>
|
|
||||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id') })} tabIndex={this.props.muted ? null : 0}>
|
|
||||||
<div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted })} data-id={status.get('id')}>
|
|
||||||
<div className='status__expand' role='presentation' />
|
|
||||||
<div className='status__info'>
|
|
||||||
<NavLink to={statusUrl} className='status__relative-time'>
|
|
||||||
<RelativeTimestamp timestamp={status.get('created_at')} futureDate />
|
|
||||||
</NavLink>
|
|
||||||
|
|
||||||
{favicon &&
|
|
||||||
<div className='status__favicon'>
|
|
||||||
<Link to={`/timeline/${domain}`}>
|
|
||||||
<img src={favicon} alt='' title={domain} />
|
|
||||||
</Link>
|
|
||||||
</div>}
|
|
||||||
|
|
||||||
<div className='status__profile'>
|
|
||||||
<div className='status__avatar'>
|
|
||||||
<NavLink to={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])}>
|
|
||||||
<Avatar account={status.get('account')} size={48} />
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
<NavLink to={`/@${status.getIn(['account', 'acct'])}`} title={status.getIn(['account', 'acct'])} className='status__display-name'>
|
|
||||||
<DisplayName account={status.get('account')} />
|
|
||||||
</NavLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StatusReplyMentions status={status} />
|
|
||||||
|
|
||||||
<StatusContent
|
|
||||||
status={status}
|
|
||||||
expanded
|
|
||||||
collapsable
|
|
||||||
/>
|
|
||||||
|
|
||||||
{status.get('media_attachments').size > 0 && (
|
|
||||||
<AttachmentThumbs
|
|
||||||
media={status.get('media_attachments')}
|
|
||||||
sensitive={status.get('sensitive')}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{status.get('poll') && <PollPreview poll={status.get('poll')} />}
|
|
||||||
|
|
||||||
<ScheduledStatusActionBar status={status} account={account} {...other} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import AttachmentThumbs from 'soapbox/components/attachment-thumbs';
|
||||||
|
import StatusContent from 'soapbox/components/status_content';
|
||||||
|
import StatusReplyMentions from 'soapbox/components/status_reply_mentions';
|
||||||
|
import { HStack } from 'soapbox/components/ui';
|
||||||
|
import AccountContainer from 'soapbox/containers/account_container';
|
||||||
|
import PollPreview from 'soapbox/features/ui/components/poll_preview';
|
||||||
|
import { useAppSelector } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import { buildStatus } from '../builder';
|
||||||
|
|
||||||
|
import ScheduledStatusActionBar from './scheduled_status_action_bar';
|
||||||
|
|
||||||
|
import type { Account as AccountEntity, Status as StatusEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
interface IScheduledStatus {
|
||||||
|
statusId: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScheduledStatus: React.FC<IScheduledStatus> = ({ statusId, ...other }) => {
|
||||||
|
const status = useAppSelector((state) => buildStatus(state, state.scheduled_statuses.get(statusId))) as StatusEntity;
|
||||||
|
|
||||||
|
if (!status) return null;
|
||||||
|
|
||||||
|
const account = status.account as AccountEntity;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('status__wrapper', `status__wrapper-${status.visibility}`, { 'status__wrapper-reply': !!status.in_reply_to_id })} tabIndex={0}>
|
||||||
|
<div className={classNames('status', `status-${status.visibility}`, { 'status-reply': !!status.in_reply_to_id })} data-id={status.id}>
|
||||||
|
<div className='mb-4'>
|
||||||
|
<HStack justifyContent='between' alignItems='start'>
|
||||||
|
<AccountContainer
|
||||||
|
key={account.id}
|
||||||
|
id={account.id}
|
||||||
|
timestamp={status.created_at}
|
||||||
|
futureTimestamp
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<StatusReplyMentions status={status} />
|
||||||
|
|
||||||
|
<StatusContent
|
||||||
|
status={status}
|
||||||
|
expanded
|
||||||
|
collapsable
|
||||||
|
/>
|
||||||
|
|
||||||
|
{status.media_attachments.size > 0 && (
|
||||||
|
<AttachmentThumbs
|
||||||
|
media={status.media_attachments}
|
||||||
|
sensitive={status.sensitive}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{status.poll && <PollPreview poll={status.poll} />}
|
||||||
|
|
||||||
|
<ScheduledStatusActionBar status={status} {...other} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScheduledStatus;
|
|
@ -1,83 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import { openModal } from 'soapbox/actions/modals';
|
|
||||||
import { cancelScheduledStatus } from 'soapbox/actions/scheduled_statuses';
|
|
||||||
import { getSettings } from 'soapbox/actions/settings';
|
|
||||||
import IconButton from 'soapbox/components/icon_button';
|
|
||||||
import SoapboxPropTypes from 'soapbox/utils/soapbox_prop_types';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
cancel: { id: 'scheduled_status.cancel', defaultMessage: 'Cancel' },
|
|
||||||
deleteConfirm: { id: 'confirmations.scheduled_status_delete.confirm', defaultMessage: 'Cancel' },
|
|
||||||
deleteHeading: { id: 'confirmations.scheduled_status_delete.heading', defaultMessage: 'Cancel scheduled post' },
|
|
||||||
deleteMessage: { id: 'confirmations.scheduled_status_delete.message', defaultMessage: 'Are you sure you want to cancel this scheduled post?' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
|
||||||
const me = state.get('me');
|
|
||||||
return {
|
|
||||||
me,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
|
||||||
onCancelClick: (status) => {
|
|
||||||
dispatch((_, getState) => {
|
|
||||||
|
|
||||||
const deleteModal = getSettings(getState()).get('deleteModal');
|
|
||||||
if (!deleteModal) {
|
|
||||||
dispatch(cancelScheduledStatus(status.get('id')));
|
|
||||||
} else {
|
|
||||||
dispatch(openModal('CONFIRM', {
|
|
||||||
icon: require('@tabler/icons/icons/calendar-stats.svg'),
|
|
||||||
heading: intl.formatMessage(messages.deleteHeading),
|
|
||||||
message: intl.formatMessage(messages.deleteMessage),
|
|
||||||
confirm: intl.formatMessage(messages.deleteConfirm),
|
|
||||||
onConfirm: () => dispatch(cancelScheduledStatus(status.get('id'))),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
class ScheduledStatusActionBar extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
status: ImmutablePropTypes.record.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
me: SoapboxPropTypes.me,
|
|
||||||
onCancelClick: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
handleCancelClick = e => {
|
|
||||||
const { status, onCancelClick } = this.props;
|
|
||||||
|
|
||||||
onCancelClick(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { intl } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='status__action-bar'>
|
|
||||||
<div className='status__button'>
|
|
||||||
<IconButton
|
|
||||||
title={intl.formatMessage(messages.cancel)}
|
|
||||||
text={intl.formatMessage(messages.cancel)}
|
|
||||||
src={require('@tabler/icons/icons/x.svg')}
|
|
||||||
onClick={this.handleCancelClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(ScheduledStatusActionBar));
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { openModal } from 'soapbox/actions/modals';
|
||||||
|
import { cancelScheduledStatus } from 'soapbox/actions/scheduled_statuses';
|
||||||
|
import { getSettings } from 'soapbox/actions/settings';
|
||||||
|
import IconButton from 'soapbox/components/icon_button';
|
||||||
|
import { HStack } from 'soapbox/components/ui';
|
||||||
|
import { useAppDispatch } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import type { Status as StatusEntity } from 'soapbox/types/entities';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
cancel: { id: 'scheduled_status.cancel', defaultMessage: 'Cancel' },
|
||||||
|
deleteConfirm: { id: 'confirmations.scheduled_status_delete.confirm', defaultMessage: 'Cancel' },
|
||||||
|
deleteHeading: { id: 'confirmations.scheduled_status_delete.heading', defaultMessage: 'Cancel scheduled post' },
|
||||||
|
deleteMessage: { id: 'confirmations.scheduled_status_delete.message', defaultMessage: 'Are you sure you want to cancel this scheduled post?' },
|
||||||
|
});
|
||||||
|
|
||||||
|
interface IScheduledStatusActionBar {
|
||||||
|
status: StatusEntity,
|
||||||
|
}
|
||||||
|
|
||||||
|
const ScheduledStatusActionBar: React.FC<IScheduledStatusActionBar> = ({ status }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const handleCancelClick = () => {
|
||||||
|
dispatch((_, getState) => {
|
||||||
|
|
||||||
|
const deleteModal = getSettings(getState()).get('deleteModal');
|
||||||
|
if (!deleteModal) {
|
||||||
|
dispatch(cancelScheduledStatus(status.id));
|
||||||
|
} else {
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
icon: require('@tabler/icons/icons/calendar-stats.svg'),
|
||||||
|
heading: intl.formatMessage(messages.deleteHeading),
|
||||||
|
message: intl.formatMessage(messages.deleteMessage),
|
||||||
|
confirm: intl.formatMessage(messages.deleteConfirm),
|
||||||
|
onConfirm: () => dispatch(cancelScheduledStatus(status.id)),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HStack justifyContent='end'>
|
||||||
|
<IconButton
|
||||||
|
title={intl.formatMessage(messages.cancel)}
|
||||||
|
text={intl.formatMessage(messages.cancel)}
|
||||||
|
src={require('@tabler/icons/icons/x.svg')}
|
||||||
|
onClick={handleCancelClick}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScheduledStatusActionBar;
|
|
@ -1,66 +0,0 @@
|
||||||
import { debounce } from 'lodash';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import React from 'react';
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
|
||||||
import { connect } from 'react-redux';
|
|
||||||
|
|
||||||
import ScrollableList from 'soapbox/components/scrollable_list';
|
|
||||||
|
|
||||||
import { fetchScheduledStatuses, expandScheduledStatuses } from '../../actions/scheduled_statuses';
|
|
||||||
import Column from '../ui/components/column';
|
|
||||||
|
|
||||||
import ScheduledStatus from './components/scheduled_status';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
heading: { id: 'column.scheduled_statuses', defaultMessage: 'Scheduled Posts' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
|
||||||
statusIds: state.getIn(['status_lists', 'scheduled_statuses', 'items']),
|
|
||||||
isLoading: state.getIn(['status_lists', 'scheduled_statuses', 'isLoading'], true),
|
|
||||||
hasMore: !!state.getIn(['status_lists', 'scheduled_statuses', 'next']),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default @connect(mapStateToProps)
|
|
||||||
@injectIntl
|
|
||||||
class ScheduledStatuses extends ImmutablePureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
dispatch: PropTypes.func.isRequired,
|
|
||||||
statusIds: ImmutablePropTypes.orderedSet.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
hasMore: PropTypes.bool,
|
|
||||||
isLoading: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { dispatch } = this.props;
|
|
||||||
dispatch(fetchScheduledStatuses());
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLoadMore = debounce(() => {
|
|
||||||
this.props.dispatch(expandScheduledStatuses());
|
|
||||||
}, 300, { leading: true })
|
|
||||||
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { intl, statusIds, hasMore, isLoading } = this.props;
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.scheduled_statuses' defaultMessage="You don't have any scheduled statuses yet. When you add one, it will show up here." />;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Column icon='calendar' label={intl.formatMessage(messages.heading)}>
|
|
||||||
<ScrollableList
|
|
||||||
scrollKey='scheduled_statuses'
|
|
||||||
emptyMessage={emptyMessage}
|
|
||||||
isLoading={isLoading}
|
|
||||||
hasMore={hasMore}
|
|
||||||
>
|
|
||||||
{statusIds.map(id => <ScheduledStatus key={id} statusId={id} />)}
|
|
||||||
</ScrollableList>
|
|
||||||
</Column>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
51
app/soapbox/features/scheduled_statuses/index.tsx
Normal file
51
app/soapbox/features/scheduled_statuses/index.tsx
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { debounce } from 'lodash';
|
||||||
|
import React from 'react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { fetchScheduledStatuses, expandScheduledStatuses } from 'soapbox/actions/scheduled_statuses';
|
||||||
|
import ScrollableList from 'soapbox/components/scrollable_list';
|
||||||
|
import { useAppSelector, useAppDispatch } from 'soapbox/hooks';
|
||||||
|
|
||||||
|
import Column from '../ui/components/column';
|
||||||
|
|
||||||
|
import ScheduledStatus from './components/scheduled_status';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
heading: { id: 'column.scheduled_statuses', defaultMessage: 'Scheduled Posts' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleLoadMore = debounce((dispatch) => {
|
||||||
|
dispatch(expandScheduledStatuses());
|
||||||
|
}, 300, { leading: true });
|
||||||
|
|
||||||
|
const ScheduledStatuses = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const statusIds = useAppSelector((state) => state.status_lists.getIn(['scheduled_statuses', 'items']));
|
||||||
|
const isLoading = useAppSelector((state) => state.status_lists.getIn(['scheduled_statuses', 'isLoading']));
|
||||||
|
const hasMore = useAppSelector((state) => !!state.status_lists.getIn(['scheduled_statuses', 'next']));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchScheduledStatuses());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const emptyMessage = <FormattedMessage id='empty_column.scheduled_statuses' defaultMessage="You don't have any scheduled statuses yet. When you add one, it will show up here." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column icon='calendar' label={intl.formatMessage(messages.heading)}>
|
||||||
|
<ScrollableList
|
||||||
|
scrollKey='scheduled_statuses'
|
||||||
|
hasMore={hasMore}
|
||||||
|
isLoading={isLoading}
|
||||||
|
onLoadMore={() => handleLoadMore(dispatch)}
|
||||||
|
emptyMessage={emptyMessage}
|
||||||
|
>
|
||||||
|
{statusIds.map((id: string) => <ScheduledStatus key={id} statusId={id} />)}
|
||||||
|
</ScrollableList>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScheduledStatuses;
|
Loading…
Reference in a new issue