From 7983a672c42f2b9f1fc474edb925f343a079c8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?nicole=20miko=C5=82ajczyk?= Date: Mon, 25 Aug 2025 00:12:29 +0200 Subject: [PATCH] pl-fe: add draft saving to compose form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: nicole mikołajczyk --- packages/pl-fe/src/components/modal-root.tsx | 8 +++- .../compose/components/compose-form.tsx | 37 +++++++++++++++++-- packages/pl-fe/src/locales/en.json | 2 + 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/packages/pl-fe/src/components/modal-root.tsx b/packages/pl-fe/src/components/modal-root.tsx index ad8939a3c..88f2e6168 100644 --- a/packages/pl-fe/src/components/modal-root.tsx +++ b/packages/pl-fe/src/components/modal-root.tsx @@ -67,10 +67,14 @@ const ModalRoot: React.FC = ({ children, onCancel, onClose, type }) openModal('CONFIRM', { heading: isEditing ? - : , + : compose.draft_id + ? + : , message: isEditing ? - : , + : compose.draft_id + ? + : , confirm: intl.formatMessage(messages.confirm), onConfirm: () => { onClose('COMPOSE'); diff --git a/packages/pl-fe/src/features/compose/components/compose-form.tsx b/packages/pl-fe/src/features/compose/components/compose-form.tsx index 6f45d3b2c..64127f5c5 100644 --- a/packages/pl-fe/src/features/compose/components/compose-form.tsx +++ b/packages/pl-fe/src/features/compose/components/compose-form.tsx @@ -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, 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, shouldCondense, autoFocus, clickab dispatch(submitCompose(id, { history }, true)); }; + const handleSaveDraft = (e?: React.FormEvent) => { + 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, shouldCondense, autoFocus, clickab if (features.richText) selectButtons.push(); if (features.postLanguages) selectButtons.push(); - 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 ( diff --git a/packages/pl-fe/src/locales/en.json b/packages/pl-fe/src/locales/en.json index 274da062a..837d0194c 100644 --- a/packages/pl-fe/src/locales/en.json +++ b/packages/pl-fe/src/locales/en.json @@ -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",