Merge branch 'schedule-input' into 'develop'

Post scheduling styles

See merge request soapbox-pub/soapbox-fe!1358
merge-requests/1362/head
marcin mikołajczak 2022-05-12 14:40:33 +00:00
commit f050a3729d
3 zmienionych plików z 118 dodań i 146 usunięć

Wyświetl plik

@ -1,129 +0,0 @@
'use strict';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import IconButton from 'soapbox/components/icon_button';
import BundleContainer from 'soapbox/features/ui/containers/bundle_container';
import { DatePicker } from 'soapbox/features/ui/util/async-components';
import { setSchedule, removeSchedule } from '../../../actions/compose';
const messages = defineMessages({
schedule: { id: 'schedule.post_time', defaultMessage: 'Post Date/Time' },
remove: { id: 'schedule.remove', defaultMessage: 'Remove schedule' },
});
const mapStateToProps = state => ({
active: state.getIn(['compose', 'schedule']) ? true : false,
scheduledAt: state.getIn(['compose', 'schedule']),
});
const mapDispatchToProps = dispatch => ({
onSchedule(date) {
dispatch(setSchedule(date));
},
onRemoveSchedule(date) {
dispatch(removeSchedule());
},
});
export default @connect(mapStateToProps, mapDispatchToProps)
@injectIntl
class ScheduleForm extends React.Component {
static propTypes = {
scheduledAt: PropTypes.instanceOf(Date),
intl: PropTypes.object.isRequired,
onSchedule: PropTypes.func.isRequired,
onRemoveSchedule: PropTypes.func.isRequired,
dispatch: PropTypes.func,
active: PropTypes.bool,
};
state = {
initialized: false,
}
setSchedule = date => {
this.props.onSchedule(date);
}
setRef = c => {
this.datePicker = c;
}
openDatePicker = () => {
if (!this.datePicker) return;
this.datePicker.setOpen(true);
}
isCurrentOrFutureDate(date) {
return date && new Date().setHours(0, 0, 0, 0) <= new Date(date).setHours(0, 0, 0, 0);
}
isFiveMinutesFromNow(time) {
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000); // now, plus five minutes (Pleroma won't schedule posts )
const selectedDate = new Date(time);
return fiveMinutesFromNow.getTime() < selectedDate.getTime();
}
handleRemove = e => {
this.props.onRemoveSchedule();
e.preventDefault();
}
initialize = () => {
const { initialized } = this.state;
if (!initialized && this.datePicker) {
this.openDatePicker();
this.setState({ initialized: true });
}
}
componentDidUpdate() {
this.initialize();
}
render() {
if (!this.props.active) {
return null;
}
const { intl, scheduledAt } = this.props;
return (
<div className={classNames('datepicker', { 'datepicker--error': !this.isFiveMinutesFromNow(scheduledAt) })}>
<div className='datepicker__hint'>
<FormattedMessage id='datepicker.hint' defaultMessage='Scheduled to post at…' />
</div>
<div className='datepicker__input'>
<BundleContainer fetchComponent={DatePicker}>
{Component => (<Component
selected={scheduledAt}
showTimeSelect
dateFormat='MMMM d, yyyy h:mm aa'
timeIntervals={15}
wrapperClassName='react-datepicker-wrapper'
onChange={this.setSchedule}
placeholderText={this.props.intl.formatMessage(messages.schedule)}
filterDate={this.isCurrentOrFutureDate}
filterTime={this.isFiveMinutesFromNow}
ref={this.setRef}
/>)}
</BundleContainer>
<div className='datepicker__cancel'>
<IconButton title={intl.formatMessage(messages.remove)} src={require('@tabler/icons/icons/x.svg')} onClick={this.handleRemove} />
</div>
</div>
</div>
);
}
}

Wyświetl plik

@ -0,0 +1,84 @@
'use strict';
import classNames from 'classnames';
import React from 'react';
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, useAppSelector } from 'soapbox/hooks';
const isCurrentOrFutureDate = (date: Date) => {
return date && new Date().setHours(0, 0, 0, 0) <= new Date(date).setHours(0, 0, 0, 0);
};
const isFiveMinutesFromNow = (time: Date) => {
const fiveMinutesFromNow = new Date(new Date().getTime() + 300000); // now, plus five minutes (Pleroma won't schedule posts )
const selectedDate = new Date(time);
return fiveMinutesFromNow.getTime() < selectedDate.getTime();
};
const messages = defineMessages({
schedule: { id: 'schedule.post_time', defaultMessage: 'Post Date/Time' },
remove: { id: 'schedule.remove', defaultMessage: 'Remove schedule' },
});
const ScheduleForm: React.FC = () => {
const dispatch = useAppDispatch();
const intl = useIntl();
const scheduledAt = useAppSelector((state) => state.compose.get('schedule'));
const active = !!scheduledAt;
const onSchedule = (date: Date) => {
dispatch(setSchedule(date));
};
const handleRemove = (e: React.MouseEvent<HTMLButtonElement>) => {
dispatch(removeSchedule());
e.preventDefault();
};
if (!active) {
return null;
}
return (
<Stack className='mb-2' space={1}>
<Text theme='muted'>
<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={classNames({
'has-error': !isFiveMinutesFromNow(scheduledAt),
})}
/>)}
</BundleContainer>
<IconButton
iconClassName='w-4 h-4'
className='bg-transparent text-gray-400 hover:text-gray-600'
src={require('@tabler/icons/icons/x.svg')}
onClick={handleRemove}
title={intl.formatMessage(messages.remove)}
/>
</HStack>
</Stack>
);
};
export default ScheduleForm;

Wyświetl plik

@ -1,14 +1,22 @@
.react-datepicker {
@apply p-4 font-sans text-xs text-gray-900 border border-solid border-gray-200 rounded-lg;
@apply dark:bg-slate-900 dark:border-slate-700 p-4 font-sans text-xs text-gray-900 dark:text-gray-300 border border-solid border-gray-200 rounded-lg;
}
.react-datepicker__input-container > input {
@apply dark:bg-slate-800 dark:text-white block w-full sm:text-sm border-gray-300 dark:border-gray-600 rounded-md focus:ring-indigo-500 focus:border-indigo-500;
&.has-error {
@apply text-red-600 border-red-600;
}
}
.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before,
.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::after {
@apply border-b-white;
@apply border-b-white dark:border-b-slate-900;
}
.react-datepicker-popper[data-placement^=bottom] .react-datepicker__triangle::before {
@apply border-b-gray-200;
@apply border-b-gray-200 dark:border-b-slate-700;
}
.react-datepicker__header:not(.react-datepicker__header--has-time-select) {
@ -16,14 +24,14 @@
}
.react-datepicker__header {
@apply bg-white border-b-0 py-1 px-0;
@apply bg-white dark:bg-slate-900 border-b-0 py-1 px-0;
// border-top-left-radius: var(--border-radius-lg);
}
.react-datepicker__current-month,
.react-datepicker-time__header,
.react-datepicker-year-header {
@apply text-gray-900 font-bold text-sm;
@apply text-gray-900 dark:text-gray-300 font-bold text-sm;
}
.react-datepicker__current-month {
@ -31,7 +39,7 @@
}
.react-datepicker__navigation {
@apply top-4 h-8 w-8 rounded hover:bg-gray-50;
@apply top-4 h-8 w-8 rounded hover:bg-gray-50 dark:hover:bg-slate-900/50;
}
.react-datepicker__navigation-icon {
@ -70,24 +78,37 @@
.react-datepicker__day-names,
.react-datepicker__week {
display: flex;
justify-content: space-between;
@apply flex justify-between;
}
.react-datepicker__time {
@apply dark:bg-slate-900;
}
.react-datepicker__time-container {
@apply dark:border-slate-700;
}
.react-datepicker__day-name,
.react-datepicker__day,
.react-datepicker__time-name {
@apply text-gray-900;
@apply text-gray-900 dark:text-gray-300;
width: 30px;
height: 30px;
line-height: 30px;
}
.react-datepicker__time-list-item--disabled,
.react-datepicker__day--disabled {
@apply text-gray-400 dark:text-gray-500;
}
.react-datepicker__day:hover,
.react-datepicker__month-text:hover,
.react-datepicker__quarter-text:hover,
.react-datepicker__year-text:hover {
@apply bg-gray-100 rounded;
.react-datepicker__year-text:hover,
.react-datepicker__time-list-item:hover {
@apply bg-gray-100 dark:bg-slate-700 rounded;
}
.react-datepicker__day--selected,
@ -102,16 +123,12 @@
.react-datepicker__year-text--selected,
.react-datepicker__year-text--in-selecting-range,
.react-datepicker__year-text--in-range {
@apply bg-primary-600 hover:bg-primary-700 text-white rounded;
}
.react-datepicker__day--disabled {
@apply text-gray-400;
@apply bg-primary-600 hover:bg-primary-700 dark:bg-slate-300 dark:hover:bg-slate-200 text-white dark:text-black rounded;
}
.react-datepicker__day--keyboard-selected,
.react-datepicker__month-text--keyboard-selected,
.react-datepicker__quarter-text--keyboard-selected,
.react-datepicker__year-text--keyboard-selected {
@apply bg-primary-50 hover:bg-primary-100 text-primary-600 dark:text-primary-400;
@apply bg-primary-50 hover:bg-primary-100 dark:bg-slate-700 dark:hover:bg-slate-600 text-primary-600 dark:text-primary-400;
}