mirror of
https://github.com/mastodon/mastodon.git
synced 2025-05-07 12:16:14 +00:00
Merge a479b2ac6b
into e6591bf322
This commit is contained in:
commit
dade625358
|
@ -81,6 +81,9 @@ export const COMPOSE_CHANGE_MEDIA_ORDER = 'COMPOSE_CHANGE_MEDIA_ORDER';
|
|||
export const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS';
|
||||
export const COMPOSE_FOCUS = 'COMPOSE_FOCUS';
|
||||
|
||||
export const COMPOSE_CHANGE_IS_SCHEDULED = 'COMPOSE_CHANGE_IS_SCHEDULED';
|
||||
export const COMPOSE_CHANGE_SCHEDULE_TIME = 'COMPOSE_CHANGE_SCHEDULE_TIME';
|
||||
|
||||
const messages = defineMessages({
|
||||
uploadErrorLimit: { id: 'upload_error.limit', defaultMessage: 'File upload limit exceeded.' },
|
||||
uploadErrorPoll: { id: 'upload_error.poll', defaultMessage: 'File upload not allowed with polls.' },
|
||||
|
@ -188,6 +191,7 @@ export function submitCompose() {
|
|||
const status = getState().getIn(['compose', 'text'], '');
|
||||
const media = getState().getIn(['compose', 'media_attachments']);
|
||||
const statusId = getState().getIn(['compose', 'id'], null);
|
||||
const is_scheduled = getState().getIn(['compose', 'is_scheduled']);
|
||||
|
||||
if ((!status || !status.length) && media.size === 0) {
|
||||
return;
|
||||
|
@ -228,6 +232,7 @@ export function submitCompose() {
|
|||
visibility: getState().getIn(['compose', 'privacy']),
|
||||
poll: getState().getIn(['compose', 'poll'], null),
|
||||
language: getState().getIn(['compose', 'language']),
|
||||
scheduled_at: is_scheduled ? getState().getIn(['compose', 'scheduled_at']) : null,
|
||||
},
|
||||
headers: {
|
||||
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
||||
|
@ -237,9 +242,22 @@ export function submitCompose() {
|
|||
browserHistory.goBack();
|
||||
}
|
||||
|
||||
|
||||
if ('scheduled_at' in response.data) {
|
||||
dispatch(showAlert({
|
||||
message: messages.saved,
|
||||
dismissAfter: 10000,
|
||||
}));
|
||||
dispatch(submitComposeSuccess({ ...response.data.params}));
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(insertIntoTagHistory(response.data.tags, status));
|
||||
dispatch(submitComposeSuccess({ ...response.data }));
|
||||
|
||||
|
||||
|
||||
|
||||
// To make the app more responsive, immediately push the status
|
||||
// into the columns
|
||||
const insertIfOnline = timelineId => {
|
||||
|
@ -835,3 +853,16 @@ export const changeMediaOrder = (a, b) => ({
|
|||
a,
|
||||
b,
|
||||
});
|
||||
|
||||
export function changeIsScheduled() {
|
||||
return {
|
||||
type: COMPOSE_CHANGE_IS_SCHEDULED,
|
||||
};
|
||||
}
|
||||
|
||||
export function changeScheduleTime(value) {
|
||||
return {
|
||||
type: COMPOSE_CHANGE_SCHEDULE_TIME,
|
||||
value,
|
||||
};
|
||||
}
|
|
@ -29,6 +29,9 @@ import { PollForm } from "./poll_form";
|
|||
import { ReplyIndicator } from './reply_indicator';
|
||||
import { UploadForm } from './upload_form';
|
||||
|
||||
import ScheduleButtonContainer from '../containers/schedule_button_container';
|
||||
import { ScheduleForm } from './schedule_form';
|
||||
|
||||
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -69,6 +72,11 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
singleColumn: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
maxChars: PropTypes.number,
|
||||
|
||||
schedule_time: PropTypes.string,
|
||||
schedule_timezone: PropTypes.string,
|
||||
is_scheduled: PropTypes.bool.isRequired,
|
||||
scheduled_at: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -295,6 +303,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
<PollButtonContainer />
|
||||
<SpoilerButtonContainer />
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
<ScheduleButtonContainer />
|
||||
<CharacterCounter max={maxChars} text={this.getFulltextForCharacterCounting()} />
|
||||
</div>
|
||||
|
||||
|
@ -306,6 +315,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<ScheduleForm />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { useDispatch, useSelector} from 'react-redux';
|
||||
|
||||
import { changeScheduleTime } from 'mastodon/actions/compose';
|
||||
|
||||
const messages = defineMessages({
|
||||
schedule_time: { id: 'compose_form.schedule_time', defaultMessage: 'This post is scheduled to be published at (UTC+8)' },
|
||||
});
|
||||
|
||||
export const ScheduleForm = () => {
|
||||
const is_scheduled = useSelector(state => state.getIn(['compose', 'is_scheduled']));
|
||||
const schedule_time = useSelector(state => state.getIn(['compose', 'schedule_time']));
|
||||
const dispatch = useDispatch();
|
||||
const intl = useIntl();
|
||||
|
||||
const handleChange = useCallback(({ target: { value } }) => {
|
||||
dispatch(changeScheduleTime(value));
|
||||
}, [dispatch]);
|
||||
|
||||
if (!is_scheduled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<label>{intl.formatMessage(messages.schedule_time)}</label>
|
||||
<input
|
||||
className='search__input'
|
||||
type='datetime-local'
|
||||
value={schedule_time}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -29,6 +29,10 @@ const mapStateToProps = state => ({
|
|||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
|
||||
is_scheduled: state.getIn(['compose', 'is_scheduled']),
|
||||
schedule_time: state.getIn(['compose', 'schedule_time']),
|
||||
schedule_timezone: state.getIn(['compose', 'schedule_timezone']),
|
||||
scheduled_at: state.getIn(['compose', 'scheduled_at']),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { injectIntl, defineMessages } from "react-intl";
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import ScheduleIcon from '@/material-icons/400-20px/schedule.svg?react';
|
||||
import { IconButton } from "@/mastodon/components/icon_button";
|
||||
|
||||
import { changeIsScheduled } from '../../../actions/compose';
|
||||
|
||||
const messages = defineMessages({
|
||||
marked: { id: 'compose_form.schedule.marked', defaultMessage: 'This post will be published at the time chosen below'},
|
||||
unmarked: { id: 'compose_form.schedule.unmarked', defaultMessage: 'This post will be published at once'},
|
||||
})
|
||||
|
||||
const mapStateToProps = (state, { intl }) => ({
|
||||
iconComponent: ScheduleIcon,
|
||||
title: intl.formatMessage(state.getIn(['compose', 'is_scheduled']) ? messages.marked : messages.unmarked),
|
||||
active: state.getIn(['compose', 'is_scheduled']),
|
||||
ariaControls: 'schedule-publish',
|
||||
size: 18,
|
||||
inverted: true,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
onClick () {
|
||||
dispatch(changeIsScheduled());
|
||||
},
|
||||
});
|
||||
|
||||
export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(IconButton));
|
|
@ -166,6 +166,9 @@
|
|||
"compose_form.publish_form": "New post",
|
||||
"compose_form.reply": "Reply",
|
||||
"compose_form.save_changes": "Update",
|
||||
"compose_form.schedule.marked": "This post will be published at the time chosen below",
|
||||
"compose_form.schedule.unmarked": "This post will be published at once",
|
||||
"compose_form.schedule_time": "This post is scheduled to be published at (UTC+8)",
|
||||
"compose_form.spoiler.marked": "Remove content warning",
|
||||
"compose_form.spoiler.unmarked": "Add content warning",
|
||||
"compose_form.spoiler_placeholder": "Content warning (optional)",
|
||||
|
|
|
@ -166,6 +166,9 @@
|
|||
"compose_form.publish_form": "发嘟",
|
||||
"compose_form.reply": "回复",
|
||||
"compose_form.save_changes": "更改",
|
||||
"compose_form.schedule.marked": "本文将在以下时间发布",
|
||||
"compose_form.schedule.unmarked": "本文将立即发布",
|
||||
"compose_form.schedule_time": "计划发文时间(北京时间)",
|
||||
"compose_form.spoiler.marked": "移除内容警告",
|
||||
"compose_form.spoiler.unmarked": "添加内容警告",
|
||||
"compose_form.spoiler_placeholder": "内容警告 (可选)",
|
||||
|
|
|
@ -50,6 +50,8 @@ import {
|
|||
COMPOSE_CHANGE_MEDIA_ORDER,
|
||||
COMPOSE_SET_STATUS,
|
||||
COMPOSE_FOCUS,
|
||||
COMPOSE_CHANGE_IS_SCHEDULED,
|
||||
COMPOSE_CHANGE_SCHEDULE_TIME
|
||||
} from '../actions/compose';
|
||||
import { REDRAFT } from '../actions/statuses';
|
||||
import { STORE_HYDRATE } from '../actions/store';
|
||||
|
@ -94,6 +96,11 @@ const initialState = ImmutableMap({
|
|||
focusY: 0,
|
||||
dirty: false,
|
||||
}),
|
||||
|
||||
schedule_time: null,
|
||||
schedule_timezone: '+08:00',
|
||||
is_scheduled: false,
|
||||
scheduled_at: null,
|
||||
});
|
||||
|
||||
const initialPoll = ImmutableMap({
|
||||
|
@ -127,6 +134,9 @@ function clearAll(state) {
|
|||
map.update('media_attachments', list => list.clear());
|
||||
map.set('poll', null);
|
||||
map.set('idempotencyKey', uuid());
|
||||
map.set('schedule_time', null);
|
||||
map.set('is_scheduled', false);
|
||||
map.set('scheduled_at', null);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -560,6 +570,18 @@ export default function compose(state = initialState, action) {
|
|||
|
||||
return list.splice(indexA, 1).splice(indexB, 0, moveItem);
|
||||
});
|
||||
case COMPOSE_CHANGE_IS_SCHEDULED:
|
||||
return state.withMutations(map => {
|
||||
map.set('is_scheduled', !state.get('is_scheduled'));
|
||||
map.set('scheduled_at', state.get('schedule_time') + ':00.0' + state.get('schedule_timezone'));
|
||||
map.set('idempotencyKey', uuid());
|
||||
});
|
||||
case COMPOSE_CHANGE_SCHEDULE_TIME:
|
||||
return state.withMutations(map => {
|
||||
map.set('schedule_time', action.value);
|
||||
map.set('scheduled_at', action.value + ':00.0' + state.get('schedule_timezone'));
|
||||
map.set('idempotencyKey', uuid());
|
||||
});
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
6
app/javascript/material-icons/400-20px/schedule.svg
Normal file
6
app/javascript/material-icons/400-20px/schedule.svg
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="20px" height="20px" viewBox="0 0 20 20" version="1.1">
|
||||
<g id="surface1">
|
||||
<path style=" stroke:none;fill-rule:nonzero;fill-opacity:1;" d="M 13.476562 8.167969 C 15.507812 8.167969 17.382812 9.214844 18.398438 10.917969 C 19.417969 12.609375 19.417969 14.722656 18.398438 16.417969 C 17.382812 18.117188 15.507812 19.167969 13.476562 19.167969 C 10.332031 19.167969 7.785156 16.699219 7.785156 13.667969 C 7.785156 10.632812 10.332031 8.167969 13.476562 8.167969 Z M 7.160156 1.25 L 7.160156 2.058594 L 12.214844 2.058594 L 12.214844 1.25 C 12.214844 1.023438 12.398438 0.832031 12.632812 0.832031 L 13.066406 0.832031 C 13.292969 0.832031 13.484375 1.015625 13.484375 1.25 L 13.484375 2.058594 L 18.125 2.058594 C 18.359375 2.058594 18.542969 2.25 18.542969 2.476562 L 18.542969 8.367188 C 18.542969 8.589844 18.359375 8.785156 18.125 8.785156 L 17.691406 8.785156 C 17.457031 8.785156 17.273438 8.601562 17.273438 8.367188 L 17.273438 6.949219 L 2.101562 6.949219 L 2.101562 17.332031 L 7.375 17.332031 C 7.609375 17.332031 7.792969 17.515625 7.792969 17.75 L 7.792969 18.140625 C 7.792969 18.375 7.609375 18.558594 7.375 18.558594 L 0.832031 18.558594 L 0.832031 2.476562 C 0.832031 2.242188 1.015625 2.058594 1.25 2.058594 L 5.890625 2.058594 L 5.890625 1.25 C 5.890625 1.023438 6.074219 0.832031 6.308594 0.832031 L 6.742188 0.832031 C 6.964844 0.832031 7.160156 1.015625 7.160156 1.25 Z M 13.476562 9.390625 C 11.890625 9.390625 10.433594 10.207031 9.640625 11.535156 C 8.851562 12.859375 8.851562 14.492188 9.640625 15.808594 C 10.433594 17.125 11.890625 17.949219 13.476562 17.949219 C 15.917969 17.949219 17.898438 16.035156 17.898438 13.675781 C 17.898438 11.316406 15.917969 9.390625 13.476562 9.390625 Z M 12.523438 11.226562 L 13.375 11.226562 C 13.609375 11.226562 13.792969 11.410156 13.792969 11.640625 L 13.792969 13.941406 C 13.792969 14.089844 13.875 14.234375 14.007812 14.308594 L 14.976562 14.851562 C 15.074219 14.902344 15.144531 14.996094 15.175781 15.105469 C 15.207031 15.210938 15.191406 15.328125 15.132812 15.425781 L 14.726562 16.117188 L 12.75 15.015625 C 12.617188 14.941406 12.535156 14.800781 12.535156 14.648438 L 12.535156 11.226562 Z M 5.890625 3.273438 L 2.101562 3.273438 L 2.101562 5.714844 L 17.273438 5.714844 L 17.273438 3.273438 L 13.484375 3.273438 L 13.484375 4.082031 C 13.484375 4.316406 13.300781 4.5 13.066406 4.5 L 12.632812 4.5 C 12.398438 4.5 12.214844 4.316406 12.214844 4.082031 L 12.214844 3.273438 L 7.160156 3.273438 L 7.160156 4.082031 C 7.160156 4.316406 6.976562 4.5 6.742188 4.5 L 6.308594 4.5 C 6.074219 4.5 5.890625 4.316406 5.890625 4.082031 Z M 5.890625 3.273438 "/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
Loading…
Reference in New Issue
Block a user