Track dirty state to skip discard prompt for unchanged drafts

When a draft is loaded and the user hasn't made any changes, closing
the composer should not show the discard prompt since nothing would
be lost.

- Add isDirty field to ComposerState
- Set isDirty: true on all content-modifying actions
- Set isDirty: false on restore_from_draft, clear, and initial state
- Update onPressCancel to only show prompt if no draft or isDirty

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Samuel Newman
2026-01-13 16:34:21 +02:00
parent 10d80dab7b
commit 092528dc05
2 changed files with 29 additions and 9 deletions
+19 -9
View File
@@ -413,21 +413,31 @@ export const ComposePost = ({
const onPressCancel = useCallback(() => { const onPressCancel = useCallback(() => {
if (textInput.current?.maybeClosePopup()) { if (textInput.current?.maybeClosePopup()) {
return return
} else if ( }
thread.posts.some(
post => const hasContent = thread.posts.some(
post.shortenedGraphemeLength > 0 || post =>
post.embed.media || post.shortenedGraphemeLength > 0 || post.embed.media || post.embed.link,
post.embed.link, )
)
) { // Show discard prompt if there's content AND either:
// - No draft is loaded (new composition)
// - Draft is loaded but has been modified
if (hasContent && (!composerState.draftId || composerState.isDirty)) {
closeAllDialogs() closeAllDialogs()
Keyboard.dismiss() Keyboard.dismiss()
discardPromptControl.open() discardPromptControl.open()
} else { } else {
onClose() onClose()
} }
}, [thread, closeAllDialogs, discardPromptControl, onClose]) }, [
thread,
composerState.draftId,
composerState.isDirty,
closeAllDialogs,
discardPromptControl,
onClose,
])
useImperativeHandle(cancelRef, () => ({onPressCancel})) useImperativeHandle(cancelRef, () => ({onPressCancel}))
+10
View File
@@ -104,6 +104,8 @@ export type ComposerState = {
mutableNeedsFocusActive: boolean mutableNeedsFocusActive: boolean
/** ID of the draft being edited, if any. Used to update existing draft on save. */ /** ID of the draft being edited, if any. Used to update existing draft on save. */
draftId?: string draftId?: string
/** Whether the composer has been modified since loading a draft. */
isDirty: boolean
} }
export type ComposerAction = export type ComposerAction =
@@ -145,6 +147,7 @@ export function composerReducer(
case 'update_postgate': { case 'update_postgate': {
return { return {
...state, ...state,
isDirty: true,
thread: { thread: {
...state.thread, ...state.thread,
postgate: action.postgate, postgate: action.postgate,
@@ -154,6 +157,7 @@ export function composerReducer(
case 'update_threadgate': { case 'update_threadgate': {
return { return {
...state, ...state,
isDirty: true,
thread: { thread: {
...state.thread, ...state.thread,
threadgate: action.threadgate, threadgate: action.threadgate,
@@ -174,6 +178,7 @@ export function composerReducer(
} }
return { return {
...state, ...state,
isDirty: true,
thread: { thread: {
...state.thread, ...state.thread,
posts: nextPosts, posts: nextPosts,
@@ -196,6 +201,7 @@ export function composerReducer(
}) })
return { return {
...state, ...state,
isDirty: true,
thread: { thread: {
...state.thread, ...state.thread,
posts: nextPosts, posts: nextPosts,
@@ -221,6 +227,7 @@ export function composerReducer(
} }
return { return {
...state, ...state,
isDirty: true,
activePostIndex: nextActivePostIndex, activePostIndex: nextActivePostIndex,
mutableNeedsFocusActive: true, mutableNeedsFocusActive: true,
thread: { thread: {
@@ -305,6 +312,7 @@ export function composerReducer(
activePostIndex: 0, activePostIndex: 0,
mutableNeedsFocusActive: true, mutableNeedsFocusActive: true,
draftId: draft.id, draftId: draft.id,
isDirty: false,
thread: { thread: {
posts, posts,
postgate: draft.postgate || state.thread.postgate, postgate: draft.postgate || state.thread.postgate,
@@ -317,6 +325,7 @@ export function composerReducer(
activePostIndex: 0, activePostIndex: 0,
mutableNeedsFocusActive: true, mutableNeedsFocusActive: true,
draftId: undefined, draftId: undefined,
isDirty: false,
thread: { thread: {
posts: [ posts: [
{ {
@@ -699,6 +708,7 @@ export function createComposerState({
return { return {
activePostIndex: 0, activePostIndex: 0,
mutableNeedsFocusActive: false, mutableNeedsFocusActive: false,
isDirty: false,
thread: { thread: {
posts: [ posts: [
{ {