Skip to content

Commit 1add962

Browse files
Merge upstream develop
2 parents 161a773 + e856cb6 commit 1add962

9 files changed

Lines changed: 225 additions & 203 deletions

File tree

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
24.17.0
1+
24.18.0

apps/polycentric/src/common/components/Avatar/Avatar.tsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { useWebHover } from '@/src/common/lib/useWebHover';
22
import { useTheme, withHexOpacity } from '@/src/common/theme';
3+
import { Image, type ImageProps } from 'expo-image';
34
import {
4-
Image,
5-
ImageProps,
6-
ImageSourcePropType,
75
Pressable,
86
PressableProps,
97
StyleSheet,
108
View,
119
type ViewProps,
1210
} from 'react-native';
13-
import { opacity } from 'react-native-reanimated/lib/typescript/Colors';
1411

1512
export type AvatarSizePreset = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'massive';
1613

@@ -28,14 +25,30 @@ export function resolveAvatarSize(size: AvatarSizePreset | number): number {
2825
}
2926

3027
interface AvatarProps extends Omit<ImageProps, 'source'> {
31-
source?: ImageSourcePropType;
28+
source?: ImageProps['source'];
29+
/**
30+
* Forces the image to clear + reload when it changes — set this when an
31+
* Avatar lives in a recycled list row (FlashList) so a reused cell never
32+
* flashes the previous identity's avatar. Defaults to the source URI.
33+
*/
34+
recyclingKey?: string;
3235
size?: AvatarSizePreset | number;
3336
containerProps?: ViewProps;
3437
onPress?: () => void;
3538
}
3639

40+
/** Pull a stable string URI out of an Image source for recycling. */
41+
function sourceUri(source: ImageProps['source']): string | undefined {
42+
if (typeof source === 'string') return source;
43+
if (source && typeof source === 'object' && 'uri' in source) {
44+
return typeof source.uri === 'string' ? source.uri : undefined;
45+
}
46+
return undefined;
47+
}
48+
3749
export function Avatar({
3850
source,
51+
recyclingKey,
3952
size: sizeProp = 'md',
4053
containerProps,
4154
onPress,
@@ -77,7 +90,8 @@ export function Avatar({
7790
<Image
7891
{...restImageProps}
7992
source={source}
80-
resizeMode={'cover'}
93+
recyclingKey={recyclingKey ?? sourceUri(source)}
94+
contentFit="cover"
8195
style={[styles.image, imageStyle]}
8296
/>
8397
<HoverOverlay visible={showHoverDim} borderRadius={size / 2} />

apps/polycentric/src/common/components/ImageViewer/ImageViewer.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import Icon from '@/src/common/components/Icon';
55
import { useCallback, useEffect, useMemo, useState } from 'react';
66
import { resolveImageSources } from './resolveImageSources';
77
import { ImageViewerInput } from './useImageViewerStore';
8+
import { Image } from 'expo-image';
89
import {
9-
Image,
1010
Platform,
1111
Pressable,
1212
StyleSheet,
@@ -270,7 +270,8 @@ export function ImageViewer({
270270
>
271271
<Image
272272
source={{ uri: current.uri }}
273-
resizeMode="contain"
273+
recyclingKey={current.uri}
274+
contentFit="contain"
274275
style={[Atoms.w_full, Atoms.h_full]}
275276
/>
276277
</Pressable>

apps/polycentric/src/features/composer/ComposerFields.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ import { Atoms, Spacing, useTheme, withHexOpacity } from '@/src/common/theme';
99
import { useState } from 'react';
1010
import {
1111
ActivityIndicator,
12-
Image,
1312
type LayoutChangeEvent,
1413
Pressable,
1514
StyleSheet,
1615
View,
1716
} from 'react-native';
17+
import { Image } from 'expo-image';
1818
import ComposerPostEmbed from './ComposerPostEmbed';
1919
import { LinkPreviewCard } from '@/src/features/post/content/LinkPreviewCard';
2020
import { v2 } from '@polycentric/react-native';
@@ -265,7 +265,8 @@ function AttachmentThumb({
265265
>
266266
<Image
267267
source={{ uri }}
268-
resizeMode="cover"
268+
recyclingKey={uri}
269+
contentFit="cover"
269270
style={{ width: '100%', height: '100%' }}
270271
/>
271272
{/* Loading / error overlay while the blobs are being processed+uploaded */}

apps/polycentric/src/features/post/PostImages.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import { Atoms } from '@/src/common/theme';
66
import { useImageViewer } from '@/src/common/components/ImageViewer';
77
import { v2 } from '@polycentric/react-native';
88
import { useCallback, useMemo } from 'react';
9-
import { Image, Pressable, View } from 'react-native';
9+
import { Image } from 'expo-image';
10+
import { Pressable, View } from 'react-native';
1011

1112
/** Target pixel size we want for displayed attachments. */
1213
const POST_IMAGE_TARGET = 512;
@@ -73,7 +74,8 @@ export function PostImages({ images }: { images: v2.ImageSet[] }) {
7374
>
7475
<Image
7576
source={{ uri: sources[0].uri }}
76-
resizeMode="cover"
77+
recyclingKey={sources[0].uri}
78+
contentFit="cover"
7779
style={[
7880
Atoms.w_full,
7981
Atoms.rounded_md,
@@ -154,7 +156,8 @@ function GridTile({
154156
>
155157
<Image
156158
source={{ uri }}
157-
resizeMode="cover"
159+
recyclingKey={uri}
160+
contentFit="cover"
158161
style={[Atoms.w_full, Atoms.h_full, { backgroundColor: TILE_BG }]}
159162
/>
160163
</Pressable>

apps/polycentric/src/features/post/content/LinkPreviewCard.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ describe('LinkPreviewCard', () => {
7373
<LinkPreviewCard link={makeLink({ image: 'https://img.test/x.png' })} />,
7474
);
7575
// The raw image URL is rewritten through the proxy, not hotlinked.
76-
expect(getByTestId('linkPreviewImage').props.source.uri).toBe(
76+
// expo-image normalizes `source={{ uri }}` into an array of sources.
77+
expect(getByTestId('linkPreviewImage').props.source[0].uri).toBe(
7778
'proxy://https://img.test/x.png',
7879
);
7980
});

apps/polycentric/src/features/post/content/LinkPreviewCard.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { Text } from '@/src/common/components/primitives';
22
import { usePolycentric } from '@/src/common/lib/polycentric-hooks';
33
import { Atoms, useTheme, withHexOpacity } from '@/src/common/theme';
44
import { v2 } from '@polycentric/react-native';
5-
import { Image, Linking, Pressable, View } from 'react-native';
5+
import { Image } from 'expo-image';
6+
import { Linking, Pressable, View } from 'react-native';
67

78
const IMAGE_BG = 'rgba(0,0,0,0.04)';
89
/** Open-Graph standard image ratio (1200×630). */
@@ -52,7 +53,8 @@ export function LinkPreviewCard({ link }: { link: v2.Link }) {
5253
<Image
5354
testID="linkPreviewImage"
5455
source={{ uri: imageUri }}
55-
resizeMode="cover"
56+
recyclingKey={imageUri}
57+
contentFit="cover"
5658
style={[
5759
Atoms.w_full,
5860
{ aspectRatio: OG_ASPECT, backgroundColor: IMAGE_BG },

packages/react-native/.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
24.17.0
1+
24.18.0

0 commit comments

Comments
 (0)