pl-fe: add draft saving to compose form

Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
This commit is contained in:
nicole mikołajczyk
2025-08-25 00:12:29 +02:00
parent 7a4d988e3c
commit 7983a672c4
3 changed files with 41 additions and 6 deletions

View File

@ -67,10 +67,14 @@ const ModalRoot: React.FC<IModalRoot> = ({ children, onCancel, onClose, type })
openModal('CONFIRM', {
heading: isEditing
? <FormattedMessage id='confirmations.cancel_editing.heading' defaultMessage='Cancel post editing' />
: <FormattedMessage id='confirmations.cancel.heading' defaultMessage='Discard post' />,
: compose.draft_id
? <FormattedMessage id='confirmations.cancel_draft.heading' defaultMessage='Discard draft changes' />
: <FormattedMessage id='confirmations.cancel.heading' defaultMessage='Discard post' />,
message: isEditing
? <FormattedMessage id='confirmations.cancel_editing.message' defaultMessage='Are you sure you want to cancel editing this post? All changes will be lost.' />
: <FormattedMessage id='confirmations.cancel.message' defaultMessage='Are you sure you want to cancel creating this post?' />,
: compose.draft_id
? <FormattedMessage id='confirmations.cancel_editing.message' defaultMessage='Are you sure you want to cancel editing this draft post? All changes will be lost.' />
: <FormattedMessage id='confirmations.cancel.message' defaultMessage='Are you sure you want to cancel creating this post?' />,
confirm: intl.formatMessage(messages.confirm),
onConfirm: () => {
onClose('COMPOSE');

View File

@ -13,7 +13,9 @@ import {
uploadCompose,
ignoreClearLinkSuggestion,
suggestClearLink,
resetCompose,
} from 'pl-fe/actions/compose';
import { saveDraftStatus } from 'pl-fe/actions/draft-statuses';
import DropdownMenu from 'pl-fe/components/dropdown-menu';
import HStack from 'pl-fe/components/ui/hstack';
import Icon from 'pl-fe/components/ui/icon';
@ -26,6 +28,8 @@ import { useCompose } from 'pl-fe/hooks/use-compose';
import { useDraggedFiles } from 'pl-fe/hooks/use-dragged-files';
import { useFeatures } from 'pl-fe/hooks/use-features';
import { useInstance } from 'pl-fe/hooks/use-instance';
import { useModalsStore } from 'pl-fe/stores/modals';
import toast from 'pl-fe/toast';
import PreviewComposeContainer from '../containers/preview-compose-container';
import QuotedStatusContainer from '../containers/quoted-status-container';
@ -69,6 +73,9 @@ const messages = defineMessages({
schedule: { id: 'compose_form.schedule', defaultMessage: 'Schedule' },
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
preview: { id: 'compose_form.preview', defaultMessage: 'Preview post' },
saveDraft: { id: 'compose_form.save_draft', defaultMessage: 'Save draft' },
draftSaved: { id: 'compose_form.save_draft.success', defaultMessage: 'Draft saved' },
view: { id: 'toast.view', defaultMessage: 'View' },
more: { id: 'compose_form.more', defaultMessage: 'More' },
});
@ -139,6 +146,7 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
const intl = useIntl();
const dispatch = useAppDispatch();
const { configuration } = useInstance();
const { closeModal } = useModalsStore();
const compose = useCompose(id);
const maxTootChars = configuration.statuses.max_characters;
@ -215,6 +223,19 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
dispatch(submitCompose(id, { history }, true));
};
const handleSaveDraft = (e?: React.FormEvent<Element>) => {
e?.preventDefault();
dispatch(saveDraftStatus(id));
closeModal('COMPOSE');
dispatch(resetCompose(id));
toast.success(messages.draftSaved, {
actionLabel: messages.view,
actionLink: '/draft_statuses',
});
};
const onSuggestionsClearRequested = () => {
dispatch(clearComposeSuggestions(id));
};
@ -317,13 +338,21 @@ const ComposeForm = <ID extends string>({ id, shouldCondense, autoFocus, clickab
if (features.richText) selectButtons.push(<ContentTypeButton key='compose-type-button' composeId={id} />);
if (features.postLanguages) selectButtons.push(<LanguageDropdown key='language-dropdown' composeId={id} />);
const actionsMenu: Menu | undefined = features.createStatusPreview ? [
{
const actionsMenu: Menu | undefined = [];
if (features.createStatusPreview) {
actionsMenu.push({
text: intl.formatMessage(messages.preview),
action: handlePreview,
icon: require('@tabler/icons/outline/eye.svg'),
},
] : undefined;
});
}
actionsMenu.push({
text: intl.formatMessage(messages.saveDraft),
action: handleSaveDraft,
icon: require('@tabler/icons/outline/notes.svg'),
});
return (
<Stack className='w-full' space={4} ref={formRef} onClick={handleClick} element='form' onSubmit={handleSubmit}>

View File

@ -571,6 +571,8 @@
"compose_form.publish_loud": "{publish}!",
"compose_form.remaining_character_count": "Remaining characters: {value} of {max}",
"compose_form.save_changes": "Save changes",
"compose_form.save_draft": "Save draft",
"compose_form.save_draft.success": "Draft saved",
"compose_form.schedule": "Schedule",
"compose_form.sensitive.marked": "Media is marked as sensitive",
"compose_form.sensitive.unmarked": "Media is not marked as sensitive",