From 10d80dab7b26623039c89925ea3d3f3936f995a6 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Tue, 13 Jan 2026 16:30:03 +0200 Subject: [PATCH] Add clear action to discard composer content When pressing the Drafts button with content in the composer, the user can choose to discard. This now properly clears the composer by dispatching a 'clear' action that resets to an empty state. - Add 'clear' action type to ComposerAction - Implement clear case in composerReducer (resets to single empty post) - Add handleClearComposer callback in Composer.tsx - Pass onDiscard prop through ComposerTopBar to DraftsButton - Call onDiscard before opening drafts dialog on discard Co-Authored-By: Claude Opus 4.5 --- src/view/com/composer/Composer.tsx | 11 +++++++- src/view/com/composer/drafts/DraftsButton.tsx | 3 ++ src/view/com/composer/state/composer.ts | 28 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 795ba9c9bf..c91a4eeab5 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -388,6 +388,11 @@ export const ComposePost = ({ return true }, [thread.posts]) + // Clear the composer (discard current content) + const handleClearComposer = React.useCallback(() => { + composerDispatch({type: 'clear'}) + }, [composerDispatch]) + const insets = useSafeAreaInsets() const viewStyles = useMemo( () => ({ @@ -814,6 +819,7 @@ export const ComposePost = ({ onPublish={onPressPublish} onSelectDraft={handleSelectDraft} onSaveDraft={saveCurrentDraft} + onDiscard={handleClearComposer} isEmpty={isComposerEmpty}> {missingAltError && } void onSelectDraft: (draft: StoredDraft) => void onSaveDraft: () => Promise + onDiscard: () => void isEmpty: boolean topBarAnimatedStyle: StyleProp children?: React.ReactNode @@ -1148,6 +1156,7 @@ function ComposerTopBar({ {isPublishing ? ( diff --git a/src/view/com/composer/drafts/DraftsButton.tsx b/src/view/com/composer/drafts/DraftsButton.tsx index 894831edb6..863e70f975 100644 --- a/src/view/com/composer/drafts/DraftsButton.tsx +++ b/src/view/com/composer/drafts/DraftsButton.tsx @@ -11,10 +11,12 @@ import {DraftsListDialog} from './DraftsListDialog' export function DraftsButton({ onSelectDraft, onSaveDraft, + onDiscard, isEmpty, }: { onSelectDraft: (draft: StoredDraft) => void onSaveDraft: () => Promise + onDiscard: () => void isEmpty: boolean }) { const {_} = useLingui() @@ -38,6 +40,7 @@ export function DraftsButton({ } const handleDiscardAndOpen = () => { + onDiscard() draftsDialogControl.open() } diff --git a/src/view/com/composer/state/composer.ts b/src/view/com/composer/state/composer.ts index aa776dab98..17d76ddd84 100644 --- a/src/view/com/composer/state/composer.ts +++ b/src/view/com/composer/state/composer.ts @@ -131,6 +131,9 @@ export type ComposerAction = /** Map of localId -> loaded media path/URL */ loadedMedia: Map } + | { + type: 'clear' + } export const MAX_IMAGES = 4 @@ -309,6 +312,31 @@ export function composerReducer( }, } } + case 'clear': { + return { + activePostIndex: 0, + mutableNeedsFocusActive: true, + draftId: undefined, + thread: { + posts: [ + { + id: nanoid(), + richtext: new RichText({text: ''}), + shortenedGraphemeLength: 0, + labels: [], + embed: { + quote: undefined, + media: undefined, + link: undefined, + }, + }, + ], + // Keep the user's default interaction settings + postgate: state.thread.postgate, + threadgate: state.thread.threadgate, + }, + } + } } }