[APP-2066] Migrate from Tenor to KLIPY (#10240)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Spence Pope
2026-04-17 08:43:13 -04:00
committed by GitHub
parent a97b15b204
commit a77b6e3525
12 changed files with 411 additions and 50 deletions
+2 -2
View File
@@ -3,7 +3,7 @@ import {Image} from 'expo-image'
import {type AppBskyFeedDefs} from '@atproto/api'
import {Trans} from '@lingui/react/macro'
import {isTenorGifUri} from '#/lib/strings/embed-player'
import {isGifEmbed} from '#/lib/strings/embed-player'
import {atoms as a, useTheme} from '#/alf'
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {Text} from '#/components/Typography'
@@ -38,7 +38,7 @@ export function Embed({
)
} else if (e.type === 'link') {
if (!e.view.external.thumb) return null
if (!isTenorGifUri(e.view.external.uri)) return null
if (!isGifEmbed(e.view.external.uri)) return null
return (
<Outer style={style}>
<GifItem
@@ -59,7 +59,10 @@ export const ExternalEmbed = ({
}
}, [link.uri, playHaptic])
if (embedPlayerParams?.source === 'tenor') {
if (
embedPlayerParams?.source === 'tenor' ||
embedPlayerParams?.source === 'klipy'
) {
const parsedAlt = parseAltFromGIFDescription(link.description)
return (
<View style={style}>
+44 -32
View File
@@ -8,16 +8,18 @@ import {
import {type TextInput, View} from 'react-native'
import {useWindowDimensions} from 'react-native'
import {Image} from 'expo-image'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Trans} from '@lingui/react/macro'
import {Trans, useLingui} from '@lingui/react/macro'
import {cleanError} from '#/lib/strings/errors'
import {
useFeaturedGifsQuery as useKlipyFeaturedGifsQuery,
useGifSearchQuery as useKlipyGifSearchQuery,
} from '#/state/queries/klipy'
import {
type Gif,
tenorUrlToBskyGifUrl,
useFeaturedGifsQuery,
useGifSearchQuery,
gifPreviewUrl,
useTenorFeaturedGifsQuery,
useTenorGifSearchQuery,
} from '#/state/queries/tenor'
import {ErrorScreen} from '#/view/com/util/error/ErrorScreen'
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
@@ -85,7 +87,8 @@ function GifList({
control: Dialog.DialogControlProps
onSelectGif: (gif: Gif) => void
}) {
const {_} = useLingui()
const ax = useAnalytics()
const {t: l} = useLingui()
const t = useTheme()
const {gtMobile} = useBreakpoints()
const textInputRef = useRef<TextInput>(null)
@@ -93,11 +96,14 @@ function GifList({
const [undeferredSearch, setSearch] = useState('')
const search = useThrottledValue(undeferredSearch, 500)
const {height} = useWindowDimensions()
const klipyEnabled = ax.features.enabled(ax.features.KlipyGifProviderEnable)
const isSearching = search.length > 0
const trendingQuery = useFeaturedGifsQuery()
const searchQuery = useGifSearchQuery(search)
const klipyTrending = useKlipyFeaturedGifsQuery({enabled: klipyEnabled})
const klipySearch = useKlipyGifSearchQuery(search, {enabled: klipyEnabled})
const tenorTrending = useTenorFeaturedGifsQuery({enabled: !klipyEnabled})
const tenorSearch = useTenorGifSearchQuery(search, {enabled: !klipyEnabled})
const {
data,
@@ -108,7 +114,13 @@ function GifList({
isPending,
isError,
refetch,
} = isSearching ? searchQuery : trendingQuery
} = klipyEnabled
? isSearching
? klipySearch
: klipyTrending
: isSearching
? tenorSearch
: tenorTrending
const flattenedData = useMemo(() => {
return data?.pages.flatMap(page => page.results) || []
@@ -158,7 +170,7 @@ function GifList({
color="secondary"
shape="round"
onPress={() => control.close()}
label={_(msg`Close GIF dialog`)}>
label={l`Close GIF dialog`}>
<ButtonIcon icon={Arrow} size="md" />
</Button>
)}
@@ -166,8 +178,8 @@ function GifList({
<TextField.Root style={[!gtMobile && IS_WEB && a.flex_1]}>
<TextField.Icon icon={Search} />
<TextField.Input
label={_(msg`Search GIFs`)}
placeholder={_(msg`Search Tenor`)}
label={l`Search GIFs`}
placeholder={klipyEnabled ? l`Search KLIPY` : l`Search Tenor`}
onChangeText={text => {
setSearch(text)
listRef.current?.scrollToOffset({offset: 0, animated: false})
@@ -185,7 +197,7 @@ function GifList({
</TextField.Root>
</View>
)
}, [gtMobile, t.atoms.bg, _, control])
}, [gtMobile, t.atoms.bg, l, control, klipyEnabled])
return (
<>
@@ -212,14 +224,18 @@ function GifList({
emptyType="results"
sideBorders={false}
topBorder={false}
errorTitle={_(msg`Failed to load GIFs`)}
errorMessage={_(msg`There was an issue connecting to Tenor.`)}
errorTitle={l`Failed to load GIFs`}
errorMessage={
klipyEnabled
? l`There was an issue connecting to KLIPY.`
: l`There was an issue connecting to Tenor.`
}
emptyMessage={
isSearching
? _(msg`No search results found for "${search}".`)
: _(
msg`No featured GIFs found. There may be an issue with Tenor.`,
)
? l`No search results found for "${search}".`
: klipyEnabled
? l`No featured GIFs found. There may be an issue with KLIPY.`
: l`No featured GIFs found. There may be an issue with Tenor.`
}
/>
)}
@@ -246,23 +262,19 @@ function GifList({
}
function DialogError({details}: {details?: string}) {
const {_} = useLingui()
const {t: l} = useLingui()
const control = Dialog.useDialogContext()
return (
<Dialog.ScrollableInner
style={a.gap_md}
label={_(msg`An error has occurred`)}>
<Dialog.ScrollableInner style={a.gap_md} label={l`An error has occurred`}>
<Dialog.Close />
<ErrorScreen
title={_(msg`Oh no!`)}
message={_(
msg`There was an unexpected issue in the application. Please let us know if this happened to you!`,
)}
title={l`Oh no!`}
message={l`There was an unexpected issue in the application. Please let us know if this happened to you!`}
details={details}
/>
<Button
label={_(msg`Close dialog`)}
label={l`Close dialog`}
onPress={() => control.close()}
color="primary"
size="large"
@@ -284,7 +296,7 @@ export function GifPreview({
}) {
const ax = useAnalytics()
const {gtTablet} = useBreakpoints()
const {_} = useLingui()
const {t: l} = useLingui()
const t = useTheme()
const onPress = useCallback(() => {
@@ -294,7 +306,7 @@ export function GifPreview({
return (
<Button
label={_(msg`Select GIF "${gif.title}"`)}
label={l`Select GIF "${gif.title}"`}
style={[a.flex_1, gtTablet ? {maxWidth: '33%'} : {maxWidth: '50%'}]}
onPress={onPress}>
{({pressed}) => (
@@ -308,7 +320,7 @@ export function GifPreview({
t.atoms.bg_contrast_25,
]}
source={{
uri: tenorUrlToBskyGifUrl(gif.media_formats.tinygif.url),
uri: gifPreviewUrl(gif.media_formats.tinygif.url),
}}
contentFit="cover"
accessibilityLabel={gif.title}