diff options
Diffstat (limited to 'src/view')
-rw-r--r-- | src/view/com/composer/Composer.tsx | 61 | ||||
-rw-r--r-- | src/view/com/composer/photos/Gallery.tsx | 4 | ||||
-rw-r--r-- | src/view/com/composer/state/composer.ts | 96 |
3 files changed, 112 insertions, 49 deletions
diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 8fdc62bc3..214f3605e 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -114,11 +114,13 @@ import {Text as NewText} from '#/components/Typography' import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet' import { ComposerAction, - ComposerDraft, composerReducer, createComposerState, EmbedDraft, MAX_IMAGES, + PostAction, + PostDraft, + ThreadDraft, } from './state/composer' import {NO_VIDEO, NoVideoState, processVideo, VideoState} from './state/video' @@ -161,11 +163,21 @@ export const ComposePost = ({ const [publishingStage, setPublishingStage] = useState('') const [error, setError] = useState('') - const [draft, dispatch] = useReducer( + const [composerState, composerDispatch] = useReducer( composerReducer, {initImageUris, initQuoteUri: initQuote?.uri, initText, initMention}, createComposerState, ) + + // TODO: Display drafts for other posts in the thread. + const draft = composerState.thread.posts[composerState.activePostIndex] + const dispatch = useCallback((postAction: PostAction) => { + composerDispatch({ + type: 'update_post', + postAction, + }) + }, []) + const richtext = draft.richtext let quote: string | undefined if (draft.embed.quote) { @@ -207,7 +219,7 @@ export const ComposePost = ({ _, ) }, - [_, agent, currentDid], + [_, agent, currentDid, dispatch], ) // Whenever we receive an initial video uri, we should immediately run compression if necessary @@ -333,7 +345,7 @@ export const ComposePost = ({ try { postUri = ( await apilib.post(agent, queryClient, { - draft: draft, + thread: composerState.thread, replyTo: replyTo?.uri, onStateChange: setPublishingStage, langs: toPostLanguages(langPrefs.postLanguage), @@ -409,7 +421,7 @@ export const ComposePost = ({ [ _, agent, - draft, + composerState.thread, extLink, images, canPost, @@ -504,8 +516,9 @@ export const ComposePost = ({ <ComposerPills isReply={!!replyTo} - draft={draft} - dispatch={dispatch} + post={draft} + thread={composerState.thread} + dispatch={composerDispatch} bottomBarAnimatedStyle={bottomBarAnimatedStyle} /> @@ -543,8 +556,8 @@ function ComposerPost({ onError, onPublish, }: { - draft: ComposerDraft - dispatch: (action: ComposerAction) => void + draft: PostDraft + dispatch: (action: PostAction) => void textInput: React.Ref<TextInputRef> isReply: boolean canRemoveQuote: boolean @@ -736,7 +749,7 @@ function ComposerEmbeds({ canRemoveQuote, }: { embed: EmbedDraft - dispatch: (action: ComposerAction) => void + dispatch: (action: PostAction) => void clearVideo: () => void canRemoveQuote: boolean }) { @@ -850,19 +863,21 @@ function ComposerEmbeds({ function ComposerPills({ isReply, - draft, + thread, + post, dispatch, bottomBarAnimatedStyle, }: { isReply: boolean - draft: ComposerDraft + thread: ThreadDraft + post: PostDraft dispatch: (action: ComposerAction) => void bottomBarAnimatedStyle: StyleProp<ViewStyle> }) { const t = useTheme() - const media = draft.embed.media + const media = post.embed.media const hasMedia = media?.type === 'images' || media?.type === 'video' - const hasLink = !!draft.embed.link + const hasLink = !!post.embed.link // Don't render anything if no pills are going to be displayed if (isReply && !hasMedia && !hasLink) { @@ -879,11 +894,11 @@ function ComposerPills({ showsHorizontalScrollIndicator={false}> {isReply ? null : ( <ThreadgateBtn - postgate={draft.postgate} + postgate={thread.postgate} onChangePostgate={nextPostgate => { dispatch({type: 'update_postgate', postgate: nextPostgate}) }} - threadgateAllowUISettings={draft.threadgate} + threadgateAllowUISettings={thread.threadgate} onChangeThreadgateAllowUISettings={nextThreadgate => { dispatch({ type: 'update_threadgate', @@ -895,9 +910,15 @@ function ComposerPills({ )} {hasMedia || hasLink ? ( <LabelsBtn - labels={draft.labels} + labels={post.labels} onChange={nextLabels => { - dispatch({type: 'update_labels', labels: nextLabels}) + dispatch({ + type: 'update_post', + postAction: { + type: 'update_labels', + labels: nextLabels, + }, + }) }} /> ) : null} @@ -914,8 +935,8 @@ function ComposerFooter({ onError, onSelectVideo, }: { - draft: ComposerDraft - dispatch: (action: ComposerAction) => void + draft: PostDraft + dispatch: (action: PostAction) => void graphemeLength: number onEmojiButtonPress: () => void onError: (error: string) => void diff --git a/src/view/com/composer/photos/Gallery.tsx b/src/view/com/composer/photos/Gallery.tsx index 5ff7042bc..e65c5407a 100644 --- a/src/view/com/composer/photos/Gallery.tsx +++ b/src/view/com/composer/photos/Gallery.tsx @@ -21,7 +21,7 @@ import {ComposerImage, cropImage} from '#/state/gallery' import {Text} from '#/view/com/util/text/Text' import {useTheme} from '#/alf' import * as Dialog from '#/components/Dialog' -import {ComposerAction} from '../state/composer' +import {PostAction} from '../state/composer' import {EditImageDialog} from './EditImageDialog' import {ImageAltTextDialog} from './ImageAltTextDialog' @@ -29,7 +29,7 @@ const IMAGE_GAP = 8 interface GalleryProps { images: ComposerImage[] - dispatch: (action: ComposerAction) => void + dispatch: (action: PostAction) => void } export let Gallery = (props: GalleryProps): React.ReactNode => { diff --git a/src/view/com/composer/state/composer.ts b/src/view/com/composer/state/composer.ts index 3c1edb6bd..958718eaf 100644 --- a/src/view/com/composer/state/composer.ts +++ b/src/view/com/composer/state/composer.ts @@ -47,19 +47,15 @@ export type EmbedDraft = { link: Link | undefined } -export type ComposerDraft = { +export type PostDraft = { richtext: RichText labels: SelfLabel[] - postgate: AppBskyFeedPostgate.Record - threadgate: ThreadgateAllowUISetting[] embed: EmbedDraft } -export type ComposerAction = +export type PostAction = | {type: 'update_richtext'; richtext: RichText} | {type: 'update_labels'; labels: SelfLabel[]} - | {type: 'update_postgate'; postgate: AppBskyFeedPostgate.Record} - | {type: 'update_threadgate'; threadgate: ThreadgateAllowUISetting[]} | {type: 'embed_add_images'; images: ComposerImage[]} | {type: 'embed_update_image'; image: ComposerImage} | {type: 'embed_remove_image'; image: ComposerImage} @@ -77,35 +73,76 @@ export type ComposerAction = | {type: 'embed_update_gif'; alt: string} | {type: 'embed_remove_gif'} +export type ThreadDraft = { + posts: PostDraft[] + postgate: AppBskyFeedPostgate.Record + threadgate: ThreadgateAllowUISetting[] +} + +export type ComposerState = { + thread: ThreadDraft + activePostIndex: number // TODO: Add actions to update this. +} + +export type ComposerAction = + | {type: 'update_postgate'; postgate: AppBskyFeedPostgate.Record} + | {type: 'update_threadgate'; threadgate: ThreadgateAllowUISetting[]} + | {type: 'update_post'; postAction: PostAction} + export const MAX_IMAGES = 4 export function composerReducer( - state: ComposerDraft, + state: ComposerState, action: ComposerAction, -): ComposerDraft { +): ComposerState { switch (action.type) { - case 'update_richtext': { + case 'update_postgate': { return { ...state, - richtext: action.richtext, + thread: { + ...state.thread, + postgate: action.postgate, + }, } } - case 'update_labels': { + case 'update_threadgate': { return { ...state, - labels: action.labels, + thread: { + ...state.thread, + threadgate: action.threadgate, + }, } } - case 'update_postgate': { + case 'update_post': { + const nextPosts = [...state.thread.posts] + nextPosts[state.activePostIndex] = postReducer( + state.thread.posts[state.activePostIndex], + action.postAction, + ) return { ...state, - postgate: action.postgate, + thread: { + ...state.thread, + posts: nextPosts, + }, } } - case 'update_threadgate': { + } +} + +function postReducer(state: PostDraft, action: PostAction): PostDraft { + switch (action.type) { + case 'update_richtext': { return { ...state, - threadgate: action.threadgate, + richtext: action.richtext, + } + } + case 'update_labels': { + return { + ...state, + labels: action.labels, } } case 'embed_add_images': { @@ -339,8 +376,6 @@ export function composerReducer( }, } } - default: - return state } } @@ -354,7 +389,7 @@ export function createComposerState({ initMention: string | undefined initImageUris: ComposerOpts['imageUris'] initQuoteUri: string | undefined -}): ComposerDraft { +}): ComposerState { let media: ImagesMedia | undefined if (initImageUris?.length) { media = { @@ -385,14 +420,21 @@ export function createComposerState({ : '', }) return { - richtext: initRichText, - labels: [], - postgate: createPostgateRecord({post: ''}), - threadgate: threadgateViewToAllowUISetting(undefined), - embed: { - quote, - media, - link: undefined, + activePostIndex: 0, + thread: { + posts: [ + { + richtext: initRichText, + labels: [], + embed: { + quote, + media, + link: undefined, + }, + }, + ], + postgate: createPostgateRecord({post: ''}), + threadgate: threadgateViewToAllowUISetting(undefined), }, } } |