Track draft ID in composer state machine
Adds draftId to ComposerState so that editing an existing draft and saving it again updates the draft rather than creating a new one. - Add draftId?: string to ComposerState type - Set draftId when restoring from draft via restore_from_draft action - Pass existingDraftId to save functions from composerState.draftId - Add PageX icon for empty drafts state Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 64 64"><path fill="#000" d="M32.457 7c1.68 0 3.29.668 4.478 1.855L49.813 21.73a6.33 6.33 0 0 1 1.854 4.479v24.458A6.333 6.333 0 0 1 45.333 57H18.666a6.334 6.334 0 0 1-6.333-6.333V13.333A6.334 6.334 0 0 1 18.666 7h13.791ZM18.666 9a4.334 4.334 0 0 0-4.333 4.333v37.334A4.334 4.334 0 0 0 18.666 55h26.667a4.333 4.333 0 0 0 4.333-4.333V26.209c0-.418-.061-.829-.177-1.223a1 1 0 0 1-.155.014H40a6.334 6.334 0 0 1-6.325-6.008l-.008-.326V9.333q0-.08.013-.156A4.3 4.3 0 0 0 32.457 9H18.666Zm18.627 22.293a1 1 0 1 1 1.414 1.414L33.414 38l5.293 5.293a1 1 0 1 1-1.414 1.414L32 39.414l-5.293 5.293a1 1 0 1 1-1.414-1.414L30.586 38l-5.293-5.293a1 1 0 1 1 1.414-1.414L32 36.586l5.293-5.293Zm-1.626-12.627.006.224A4.333 4.333 0 0 0 40 23h8.253L35.667 10.414v8.252Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 822 B |
@@ -0,0 +1,6 @@
|
|||||||
|
import {createSinglePathSVG} from './TEMPLATE'
|
||||||
|
|
||||||
|
export const PageX_Stroke2_Corner0_Rounded_Large = createSinglePathSVG({
|
||||||
|
viewBox: '0 0 64 64',
|
||||||
|
path: 'M32.457 7c1.68 0 3.29.668 4.478 1.855L49.813 21.73a6.33 6.33 0 0 1 1.854 4.479v24.458A6.333 6.333 0 0 1 45.333 57H18.666a6.334 6.334 0 0 1-6.333-6.333V13.333A6.334 6.334 0 0 1 18.666 7h13.791ZM18.666 9a4.334 4.334 0 0 0-4.333 4.333v37.334A4.334 4.334 0 0 0 18.666 55h26.667a4.333 4.333 0 0 0 4.333-4.333V26.209c0-.418-.061-.829-.177-1.223a1 1 0 0 1-.155.014H40a6.334 6.334 0 0 1-6.325-6.008l-.008-.326V9.333q0-.08.013-.156A4.3 4.3 0 0 0 32.457 9H18.666Zm18.627 22.293a1 1 0 1 1 1.414 1.414L33.414 38l5.293 5.293a1 1 0 1 1-1.414 1.414L32 39.414l-5.293 5.293a1 1 0 1 1-1.414-1.414L30.586 38l-5.293-5.293a1 1 0 1 1 1.414-1.414L32 36.586l5.293-5.293Zm-1.626-12.627.006.224A4.333 4.333 0 0 0 40 23h8.253L35.667 10.414v8.252Z',
|
||||||
|
})
|
||||||
@@ -330,7 +330,7 @@ export const ComposePost = ({
|
|||||||
// Load media from local storage
|
// Load media from local storage
|
||||||
const loadedMedia = await loadDraftMedia(currentDid, draft)
|
const loadedMedia = await loadDraftMedia(currentDid, draft)
|
||||||
|
|
||||||
// Dispatch restore action
|
// Dispatch restore action (this also sets draftId in state)
|
||||||
composerDispatch({
|
composerDispatch({
|
||||||
type: 'restore_from_draft',
|
type: 'restore_from_draft',
|
||||||
draft,
|
draft,
|
||||||
@@ -352,6 +352,7 @@ export const ComposePost = ({
|
|||||||
await saveDraft({
|
await saveDraft({
|
||||||
composerState,
|
composerState,
|
||||||
replyTo,
|
replyTo,
|
||||||
|
existingDraftId: composerState.draftId,
|
||||||
})
|
})
|
||||||
onClose()
|
onClose()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -365,6 +366,7 @@ export const ComposePost = ({
|
|||||||
await saveDraft({
|
await saveDraft({
|
||||||
composerState,
|
composerState,
|
||||||
replyTo,
|
replyTo,
|
||||||
|
existingDraftId: composerState.draftId,
|
||||||
})
|
})
|
||||||
}, [saveDraft, composerState, replyTo])
|
}, [saveDraft, composerState, replyTo])
|
||||||
|
|
||||||
|
|||||||
@@ -74,12 +74,12 @@ export function DraftsButton({
|
|||||||
</Prompt.DescriptionText>
|
</Prompt.DescriptionText>
|
||||||
<Prompt.Actions>
|
<Prompt.Actions>
|
||||||
<Prompt.Action
|
<Prompt.Action
|
||||||
cta={_(msg`Save & View Drafts`)}
|
cta={_(msg`Save changes`)}
|
||||||
onPress={handleSaveAndOpen}
|
onPress={handleSaveAndOpen}
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
<Prompt.Action
|
<Prompt.Action
|
||||||
cta={_(msg`Discard & View Drafts`)}
|
cta={_(msg`Discard`)}
|
||||||
onPress={handleDiscardAndOpen}
|
onPress={handleDiscardAndOpen}
|
||||||
color="negative"
|
color="negative"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import {
|
|||||||
useLoadDraft,
|
useLoadDraft,
|
||||||
} from '#/state/drafts'
|
} from '#/state/drafts'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon} from '#/components/Button'
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
import * as Dialog from '#/components/Dialog'
|
import * as Dialog from '#/components/Dialog'
|
||||||
import {ArrowLeft_Stroke2_Corner0_Rounded as ArrowLeftIcon} from '#/components/icons/Arrow'
|
import {PageX_Stroke2_Corner0_Rounded_Large} from '#/components/icons/PageX'
|
||||||
import {Loader} from '#/components/Loader'
|
import {Loader} from '#/components/Loader'
|
||||||
import {Text} from '#/components/Typography'
|
import {EmptyState} from '../../util/EmptyState'
|
||||||
import {DraftItem} from './DraftItem'
|
import {DraftItem} from './DraftItem'
|
||||||
|
|
||||||
export function DraftsListDialog({
|
export function DraftsListDialog({
|
||||||
@@ -57,9 +57,10 @@ export function DraftsListDialog({
|
|||||||
onPress={() => control.close()}
|
onPress={() => control.close()}
|
||||||
size="small"
|
size="small"
|
||||||
color="primary"
|
color="primary"
|
||||||
variant="ghost"
|
variant="ghost">
|
||||||
shape="round">
|
<ButtonText style={[a.text_md]}>
|
||||||
<ButtonIcon icon={ArrowLeftIcon} size="md" />
|
<Trans>Back</Trans>
|
||||||
|
</ButtonText>
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
[control, _],
|
[control, _],
|
||||||
@@ -98,17 +99,15 @@ export function DraftsListDialog({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<View style={[a.py_xl, a.align_center]}>
|
<EmptyState
|
||||||
<Text style={[t.atoms.text_contrast_medium]}>
|
icon={PageX_Stroke2_Corner0_Rounded_Large}
|
||||||
<Trans>No drafts saved</Trans>
|
message={_(msg`No drafts yet`)}
|
||||||
</Text>
|
/>
|
||||||
</View>
|
|
||||||
)
|
)
|
||||||
}, [isLoading, t.atoms.text_contrast_medium])
|
}, [isLoading, _])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
<Dialog.Outer control={control}>
|
||||||
<Dialog.Handle />
|
|
||||||
<Dialog.InnerFlatList
|
<Dialog.InnerFlatList
|
||||||
data={drafts ?? []}
|
data={drafts ?? []}
|
||||||
renderItem={renderItem}
|
renderItem={renderItem}
|
||||||
@@ -116,9 +115,7 @@ export function DraftsListDialog({
|
|||||||
ListHeaderComponent={listHeader}
|
ListHeaderComponent={listHeader}
|
||||||
ListEmptyComponent={emptyComponent}
|
ListEmptyComponent={emptyComponent}
|
||||||
stickyHeaderIndices={[0]}
|
stickyHeaderIndices={[0]}
|
||||||
contentContainerStyle={[a.pb_lg]}
|
style={t.atoms.bg_contrast_25}
|
||||||
ItemSeparatorComponent={() => <View style={[{height: 8}]} />}
|
|
||||||
style={[a.px_lg]}
|
|
||||||
/>
|
/>
|
||||||
</Dialog.Outer>
|
</Dialog.Outer>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -102,6 +102,8 @@ export type ComposerState = {
|
|||||||
thread: ThreadDraft
|
thread: ThreadDraft
|
||||||
activePostIndex: number
|
activePostIndex: number
|
||||||
mutableNeedsFocusActive: boolean
|
mutableNeedsFocusActive: boolean
|
||||||
|
/** ID of the draft being edited, if any. Used to update existing draft on save. */
|
||||||
|
draftId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComposerAction =
|
export type ComposerAction =
|
||||||
@@ -299,6 +301,7 @@ export function composerReducer(
|
|||||||
return {
|
return {
|
||||||
activePostIndex: 0,
|
activePostIndex: 0,
|
||||||
mutableNeedsFocusActive: true,
|
mutableNeedsFocusActive: true,
|
||||||
|
draftId: draft.id,
|
||||||
thread: {
|
thread: {
|
||||||
posts,
|
posts,
|
||||||
postgate: draft.postgate || state.thread.postgate,
|
postgate: draft.postgate || state.thread.postgate,
|
||||||
|
|||||||
Reference in New Issue
Block a user