import {useCallback, useEffect, useMemo} from 'react' import {Keyboard, View} from 'react-native' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' import {useCallOnce} from '#/lib/once' import {EmptyState} from '#/view/com/util/EmptyState' import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' import {PageX_Stroke2_Corner0_Rounded_Large as PageXIcon} from '#/components/icons/PageX' import {ListFooter} from '#/components/Lists' import {Loader} from '#/components/Loader' import {Text} from '#/components/Typography' import {useAnalytics} from '#/analytics' import {IS_NATIVE} from '#/env' import {DraftItem} from './DraftItem' import {useDeleteDraftMutation, useDraftsQuery} from './state/queries' import {type DraftSummary} from './state/schema' export function DraftsListDialog({ control, onSelectDraft, }: { control: Dialog.DialogControlProps onSelectDraft: (draft: DraftSummary) => void }) { const {_} = useLingui() const t = useTheme() const {gtPhone} = useBreakpoints() const ax = useAnalytics() const {data, isLoading, hasNextPage, isFetchingNextPage, fetchNextPage} = useDraftsQuery() const {mutate: deleteDraft} = useDeleteDraftMutation() const drafts = useMemo( () => data?.pages.flatMap(page => page.drafts) ?? [], [data], ) // Fire draft:listOpen metric when dialog opens and data is loaded const draftCount = drafts.length const isDataReady = !isLoading && data !== undefined const onDraftListOpen = useCallOnce() useEffect(() => { if (isDataReady) { onDraftListOpen(() => { ax.metric('draft:listOpen', { draftCount, }) }) } }, [onDraftListOpen, isDataReady, draftCount, ax]) const handleSelectDraft = useCallback( (summary: DraftSummary) => { // Dismiss keyboard immediately to prevent flicker. Without this, // the text input regains focus (showing the keyboard) after the // drafts sheet closes, then loses it again when the post component // remounts with the draft content, causing a show-hide-show cycle -sfn Keyboard.dismiss() control.close(() => { onSelectDraft(summary) }) }, [control, onSelectDraft], ) const handleDeleteDraft = useCallback( (draftSummary: DraftSummary) => { // Fire draft:delete metric const draftAgeMs = Date.now() - new Date(draftSummary.createdAt).getTime() ax.metric('draft:delete', { logContext: 'DraftsList', draftAgeMs, }) deleteDraft({draftId: draftSummary.id, draft: draftSummary.draft}) }, [deleteDraft, ax], ) const backButton = useCallback( () => ( ), [control, _], ) const renderItem = useCallback( ({item}: {item: DraftSummary}) => { return ( ) }, [handleSelectDraft, handleDeleteDraft, gtPhone], ) const header = useMemo( () => ( Drafts ), [backButton], ) const onEndReached = useCallback(() => { if (hasNextPage && !isFetchingNextPage) { void fetchNextPage() } }, [hasNextPage, isFetchingNextPage, fetchNextPage]) const emptyComponent = useMemo(() => { if (isLoading) { return ( ) } return ( ) }, [isLoading, _]) const footerComponent = useMemo( () => ( <> {drafts.length > 5 && ( So many thoughts, you should post one )} ), [isFetchingNextPage, hasNextPage, drafts.length, t], ) return ( {/* We really really need to figure out a nice, consistent API for doing a header cross-platform -sfn */} {IS_NATIVE && header} item.id} ListHeaderComponent={web(header)} stickyHeaderIndices={web([0])} ListEmptyComponent={emptyComponent} ListFooterComponent={footerComponent} onEndReached={onEndReached} onEndReachedThreshold={0.5} style={[a.px_0, web({minHeight: 500})]} webInnerContentContainerStyle={[a.py_0]} contentContainerStyle={[a.pb_xl]} /> ) }