import React, {
useCallback,
useDeferredValue,
useMemo,
useRef,
useState,
} from 'react'
import {TextInput, View} from 'react-native'
import {Image} from 'expo-image'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {GIPHY_PRIVACY_POLICY} from '#/lib/constants'
import {logEvent} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors'
import {isWeb} from '#/platform/detection'
import {
useExternalEmbedsPrefs,
useSetExternalEmbedPref,
} from '#/state/preferences'
import {Gif, useGifphySearch, useGiphyTrending} from '#/state/queries/giphy'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import * as Dialog from '#/components/Dialog'
import * as TextField from '#/components/forms/TextField'
import {ArrowLeft_Stroke2_Corner0_Rounded as Arrow} from '#/components/icons/Arrow'
import {MagnifyingGlass2_Stroke2_Corner0_Rounded as Search} from '#/components/icons/MagnifyingGlass2'
import {InlineLinkText} from '#/components/Link'
import {Button, ButtonIcon, ButtonText} from '../Button'
import {ListFooter, ListMaybePlaceholder} from '../Lists'
import {Text} from '../Typography'
export function GifSelectDialog({
control,
onClose,
onSelectGif: onSelectGifProp,
}: {
control: Dialog.DialogControlProps
onClose: () => void
onSelectGif: (gif: Gif) => void
}) {
const externalEmbedsPrefs = useExternalEmbedsPrefs()
const onSelectGif = useCallback(
(gif: Gif) => {
control.close(() => onSelectGifProp(gif))
},
[control, onSelectGifProp],
)
let content = null
let snapPoints
switch (externalEmbedsPrefs?.giphy) {
case 'show':
content =
snapPoints = ['100%']
break
case 'hide':
default:
content =
break
}
return (
{content}
)
}
function GifList({
control,
onSelectGif,
}: {
control: Dialog.DialogControlProps
onSelectGif: (gif: Gif) => void
}) {
const {_} = useLingui()
const t = useTheme()
const {gtMobile} = useBreakpoints()
const ref = useRef(null)
const [undeferredSearch, setSearch] = useState('')
const search = useDeferredValue(undeferredSearch)
const isSearching = search.length > 0
const trendingQuery = useGiphyTrending()
const searchQuery = useGifphySearch(search)
const {
data,
fetchNextPage,
isFetchingNextPage,
hasNextPage,
error,
isLoading,
isError,
refetch,
} = isSearching ? searchQuery : trendingQuery
const flattenedData = useMemo(() => {
const uniquenessSet = new Set()
function filter(gif: Gif) {
if (!gif) return false
if (uniquenessSet.has(gif.id)) {
return false
}
uniquenessSet.add(gif.id)
return true
}
return data?.pages.flatMap(page => page.data.filter(filter)) || []
}, [data])
const renderItem = useCallback(
({item}: {item: Gif}) => {
return
},
[onSelectGif],
)
const onEndReached = React.useCallback(() => {
if (isFetchingNextPage || !hasNextPage || error) return
fetchNextPage()
}, [isFetchingNextPage, hasNextPage, error, fetchNextPage])
const hasData = flattenedData.length > 0
const onGoBack = useCallback(() => {
if (isSearching) {
// clear the input and reset the state
ref.current?.clear()
setSearch('')
} else {
control.close()
}
}, [control, isSearching])
const listHeader = useMemo(() => {
return (
{/* cover top corners */}
{!gtMobile && isWeb && (
)}
{
if (nativeEvent.key === 'Escape') {
control.close()
}
}}
/>
)
}, [gtMobile, t.atoms.bg, _, control])
return (
<>
{gtMobile && }
{listHeader}
{!hasData && (
)}
>
}
stickyHeaderIndices={[0]}
onEndReached={onEndReached}
onEndReachedThreshold={4}
keyExtractor={(item: Gif) => item.id}
// @ts-expect-error web only
style={isWeb && {minHeight: '100vh'}}
ListFooterComponent={
hasData ? (
) : null
}
/>
>
)
}
function GifPreview({
gif,
onSelectGif,
}: {
gif: Gif
onSelectGif: (gif: Gif) => void
}) {
const {gtTablet} = useBreakpoints()
const {_} = useLingui()
const t = useTheme()
const onPress = useCallback(() => {
logEvent('composer:gif:select', {})
onSelectGif(gif)
}, [onSelectGif, gif])
return (
)
}
function GiphyConsentPrompt({control}: {control: Dialog.DialogControlProps}) {
const {_} = useLingui()
const t = useTheme()
const {gtMobile} = useBreakpoints()
const setExternalEmbedPref = useSetExternalEmbedPref()
const onShowPress = useCallback(() => {
setExternalEmbedPref('giphy', 'show')
}, [setExternalEmbedPref])
const onHidePress = useCallback(() => {
setExternalEmbedPref('giphy', 'hide')
control.close()
}, [control, setExternalEmbedPref])
const gtMobileWeb = gtMobile && isWeb
return (
Permission to use GIPHY
Bluesky uses GIPHY to provide the GIF selector feature.
GIPHY may collect information about you and your device. You can
find out more in their{' '}
control.close()}>
privacy policy
.
)
}