diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..53998ac58c --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(yarn typecheck *)", + "Bash(yarn lint *)", + "Bash(yarn test *)" + ] + } +} diff --git a/__tests__/lib/string.test.ts b/__tests__/lib/string.test.ts index 38966c35cb..8a56423796 100644 --- a/__tests__/lib/string.test.ts +++ b/__tests__/lib/string.test.ts @@ -8,8 +8,7 @@ import { parseStarterPackUri, } from '#/lib/strings/starter-pack' import {messages} from '#/locale/locales/en/messages' -import {klipyUrlToBskyGifUrl} from '#/state/queries/klipy' -import {tenorUrlToBskyGifUrl} from '#/state/queries/tenor' +import {klipyUrlToBskyGifUrl} from '#/features/gifPicker/utils' import {cleanError} from '../../src/lib/strings/errors' import {createFullHandle, makeValidHandle} from '../../src/lib/strings/handles' import {enforceLen} from '../../src/lib/strings/helpers' @@ -1072,21 +1071,6 @@ describe('createStarterPackGooglePlayUri', () => { }) }) -describe('tenorUrlToBskyGifUrl', () => { - const inputs = [ - 'https://media.tenor.com/someID_AAAAC/someName.gif', - 'https://media.tenor.com/someID/someName.gif', - ] - - it.each(inputs)( - 'returns url with t.gifs.bsky.app as hostname for input url', - input => { - const out = tenorUrlToBskyGifUrl(input) - expect(out.startsWith('https://t.gifs.bsky.app/')).toEqual(true) - }, - ) -}) - describe('klipyUrlToBskyGifUrl', () => { const inputs = [ 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif', diff --git a/src/features/gifPicker/GifPickerDialog.tsx b/src/features/gifPicker/GifPickerDialog.tsx index efd89edbf2..24476d08fd 100644 --- a/src/features/gifPicker/GifPickerDialog.tsx +++ b/src/features/gifPicker/GifPickerDialog.tsx @@ -100,6 +100,10 @@ function GifPickerBody({ const items = isRecentsActive ? getRecents() : networkItems const hasData = items.length > 0 + // Remount the grid when the data source changes so FlatList doesn't carry + // stale virtualized cells between e.g. a search and the recents list. + const viewKey = isRecentsActive ? 'recents' : `network:${effectiveSearch}` + const onEndReached = () => { if (isRecentsActive) return if (isFetchingNextPage || !hasNextPage || error) return @@ -172,6 +176,7 @@ function GifPickerBody({ hasData={hasData} isFetchingNextPage={!isRecentsActive && isFetchingNextPage} error={isRecentsActive ? null : error} + viewKey={viewKey} fetchNextPage={fetchNextPage} onEndReached={onEndReached} onSelectGif={handleSelectGif} diff --git a/src/features/gifPicker/components/GifCategoryPills.tsx b/src/features/gifPicker/components/GifCategoryPills.tsx index 9c0e2f841c..d11aa25b53 100644 --- a/src/features/gifPicker/components/GifCategoryPills.tsx +++ b/src/features/gifPicker/components/GifCategoryPills.tsx @@ -1,10 +1,10 @@ import {View} from 'react-native' -import {i18n, type MessageDescriptor} from '@lingui/core' +import {type MessageDescriptor} from '@lingui/core' import {msg} from '@lingui/core/macro' -import {useLingui} from '@lingui/react' +import {useLingui} from '@lingui/react/macro' import {atoms as a} from '#/alf' -import {Button, useSharedButtonTextStyles} from '#/components/Button' +import {Button, ButtonIcon} from '#/components/Button' import {Celebrate_Stroke2_Corner0_Rounded as Celebrate} from '#/components/icons/Celebrate' import {Clock_Stroke2_Corner0_Rounded as Clock} from '#/components/icons/Clock' import {type Props as SVGIconProps} from '#/components/icons/common' @@ -16,8 +16,6 @@ import {Heart2_Stroke2_Corner0_Rounded as Heart} from '#/components/icons/Heart2 import {Shaka_Stroke2_Corner0_Rounded as Shaka} from '#/components/icons/Shaka' import {Trending3_Stroke2_Corner1_Rounded as Trending} from '#/components/icons/Trending' -const ICON_SIZE = 20 - export type GifCategory = { id: string icon: React.ComponentType @@ -49,10 +47,7 @@ export function GifCategoryPills({ onSelect: (category: GifCategory) => void hasRecents: boolean }) { - // Subscribe to locale changes so i18n._() returns the current translation. - // The lingui-msg-rule lint rule forbids _() with variables, so we use - // i18n._() directly to translate the MessageDescriptor from GIF_CATEGORIES. - useLingui() + const {i18n} = useLingui() return ( onSelect(category)} size="small" - variant={isActive ? 'solid' : 'ghost'} - color="secondary" + color={isActive ? 'secondary_inverted' : 'secondary'} shape="round"> - + ) })} ) } - -function PillIcon({icon: Icon}: {icon: React.ComponentType}) { - const textStyles = useSharedButtonTextStyles() - return ( - - ) -} diff --git a/src/features/gifPicker/components/GifPickerErrorBoundary.tsx b/src/features/gifPicker/components/GifPickerErrorBoundary.tsx index 37f6f2c246..d2fd469fec 100644 --- a/src/features/gifPicker/components/GifPickerErrorBoundary.tsx +++ b/src/features/gifPicker/components/GifPickerErrorBoundary.tsx @@ -1,6 +1,4 @@ -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 {ErrorScreen} from '#/view/com/util/error/ErrorScreen' import {atoms as a} from '#/alf' @@ -8,27 +6,22 @@ import {Button, ButtonText} from '#/components/Button' import * as Dialog from '#/components/Dialog' export function GifPickerErrorBoundary({details}: {details?: string}) { - const {_} = useLingui() + const {t: l} = useLingui() const control = Dialog.useDialogContext() return ( - + )} @@ -51,8 +49,8 @@ export function GifPickerHeader({ void }) { const ax = useAnalytics() - const {_} = useLingui() + const {t: l} = useLingui() const t = useTheme() const [width, height] = gif.media_formats.tinygif.dims @@ -29,7 +28,7 @@ export function GifPickerItem({ return (