diff options
Diffstat (limited to 'src/view/com/composer/state/composer.ts')
-rw-r--r-- | src/view/com/composer/state/composer.ts | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/view/com/composer/state/composer.ts b/src/view/com/composer/state/composer.ts new file mode 100644 index 000000000..a23a5d8c8 --- /dev/null +++ b/src/view/com/composer/state/composer.ts @@ -0,0 +1,199 @@ +import {ImagePickerAsset} from 'expo-image-picker' + +import {ComposerImage, createInitialImages} from '#/state/gallery' +import {ComposerOpts} from '#/state/shell/composer' +import {createVideoState, VideoAction, videoReducer, VideoState} from './video' + +type PostRecord = { + uri: string +} + +type ImagesMedia = { + type: 'images' + images: ComposerImage[] + labels: string[] +} + +type VideoMedia = { + type: 'video' + video: VideoState +} + +type ComposerEmbed = { + // TODO: Other record types. + record: PostRecord | undefined + // TODO: Other media types. + media: ImagesMedia | VideoMedia | undefined +} + +export type ComposerState = { + // TODO: Other draft data. + embed: ComposerEmbed +} + +export type ComposerAction = + | {type: 'embed_add_images'; images: ComposerImage[]} + | {type: 'embed_update_image'; image: ComposerImage} + | {type: 'embed_remove_image'; image: ComposerImage} + | { + type: 'embed_add_video' + asset: ImagePickerAsset + abortController: AbortController + } + | {type: 'embed_remove_video'} + | {type: 'embed_update_video'; videoAction: VideoAction} + +const MAX_IMAGES = 4 + +export function composerReducer( + state: ComposerState, + action: ComposerAction, +): ComposerState { + switch (action.type) { + case 'embed_add_images': { + if (action.images.length === 0) { + return state + } + const prevMedia = state.embed.media + let nextMedia = prevMedia + if (!prevMedia) { + nextMedia = { + type: 'images', + images: action.images.slice(0, MAX_IMAGES), + labels: [], + } + } else if (prevMedia.type === 'images') { + nextMedia = { + ...prevMedia, + images: [...prevMedia.images, ...action.images].slice(0, MAX_IMAGES), + } + } + return { + ...state, + embed: { + ...state.embed, + media: nextMedia, + }, + } + } + case 'embed_update_image': { + const prevMedia = state.embed.media + if (prevMedia?.type === 'images') { + const updatedImage = action.image + const nextMedia = { + ...prevMedia, + images: prevMedia.images.map(img => { + if (img.source.id === updatedImage.source.id) { + return updatedImage + } + return img + }), + } + return { + ...state, + embed: { + ...state.embed, + media: nextMedia, + }, + } + } + return state + } + case 'embed_remove_image': { + const prevMedia = state.embed.media + if (prevMedia?.type === 'images') { + const removedImage = action.image + let nextMedia: ImagesMedia | undefined = { + ...prevMedia, + images: prevMedia.images.filter(img => { + return img.source.id !== removedImage.source.id + }), + } + if (nextMedia.images.length === 0) { + nextMedia = undefined + } + return { + ...state, + embed: { + ...state.embed, + media: nextMedia, + }, + } + } + return state + } + case 'embed_add_video': { + const prevMedia = state.embed.media + let nextMedia = prevMedia + if (!prevMedia) { + nextMedia = { + type: 'video', + video: createVideoState(action.asset, action.abortController), + } + } + return { + ...state, + embed: { + ...state.embed, + media: nextMedia, + }, + } + } + case 'embed_update_video': { + const videoAction = action.videoAction + const prevMedia = state.embed.media + let nextMedia = prevMedia + if (prevMedia?.type === 'video') { + nextMedia = { + ...prevMedia, + video: videoReducer(prevMedia.video, videoAction), + } + } + return { + ...state, + embed: { + ...state.embed, + media: nextMedia, + }, + } + } + case 'embed_remove_video': { + const prevMedia = state.embed.media + let nextMedia = prevMedia + if (prevMedia?.type === 'video') { + nextMedia = undefined + } + return { + ...state, + embed: { + ...state.embed, + media: nextMedia, + }, + } + } + default: + return state + } +} + +export function createComposerState({ + initImageUris, +}: { + initImageUris: ComposerOpts['imageUris'] +}): ComposerState { + let media: ImagesMedia | undefined + if (initImageUris?.length) { + media = { + type: 'images', + images: createInitialImages(initImageUris), + labels: [], + } + } + // TODO: initial video. + return { + embed: { + record: undefined, + media, + }, + } +} |