@oxyhq/services 22.6.3 → 22.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/so/oxy/identity/OxyIdentityStore.kt +109 -14
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +66 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/AnimatedButton.js +7 -1
- package/lib/commonjs/ui/components/fileManagement/AnimatedButton.js.map +1 -1
- package/lib/commonjs/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +43 -0
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +17 -106
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +133 -53
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +64 -0
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/components/fileManagement/AnimatedButton.js +7 -1
- package/lib/module/ui/components/fileManagement/AnimatedButton.js.map +1 -1
- package/lib/module/ui/components/photogrid/JustifiedPhotoGrid.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +44 -1
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +13 -102
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +133 -53
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +12 -0
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/AnimatedButton.d.ts +1 -0
- package/lib/typescript/commonjs/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts +0 -1
- package/lib/typescript/commonjs/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +12 -0
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/AnimatedButton.d.ts +1 -0
- package/lib/typescript/module/ui/components/fileManagement/AnimatedButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts +0 -1
- package/lib/typescript/module/ui/components/photogrid/JustifiedPhotoGrid.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +2 -2
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/PhotoPickerSection.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/ui/boot/runProviderColdBoot.ts +67 -0
- package/src/ui/components/fileManagement/AnimatedButton.tsx +9 -1
- package/src/ui/components/photogrid/JustifiedPhotoGrid.tsx +0 -1
- package/src/ui/context/OxyContext.tsx +43 -1
- package/src/ui/screens/FileManagementScreen.tsx +17 -112
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +132 -51
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
Animated,
|
|
13
13
|
Easing,
|
|
14
14
|
AccessibilityInfo,
|
|
15
|
+
Platform,
|
|
15
16
|
useWindowDimensions,
|
|
16
17
|
} from 'react-native';
|
|
17
18
|
import { Image as ExpoImage } from 'expo-image';
|
|
@@ -39,6 +40,7 @@ import { useFileUploadState } from './fileManagement/hooks/useFileUploadState';
|
|
|
39
40
|
import PhotoPickerView from './fileManagement/PhotoPickerSection';
|
|
40
41
|
import FileListSection, { type FileListItem } from './fileManagement/FileListSection';
|
|
41
42
|
import UploadBar from './fileManagement/UploadBar';
|
|
43
|
+
import { AnimatedButton } from '../components/fileManagement/AnimatedButton';
|
|
42
44
|
|
|
43
45
|
// Genuinely-inline-only styles: `viewModeButton` is spread into an Animated.View
|
|
44
46
|
// style array (interpolated backgroundColor), and the photo tiles are
|
|
@@ -57,60 +59,6 @@ const screenStyles = StyleSheet.create({
|
|
|
57
59
|
justifiedPhotoImage: { width: '100%', height: '100%', borderRadius: 6 },
|
|
58
60
|
});
|
|
59
61
|
|
|
60
|
-
// Animated button component for smooth transitions
|
|
61
|
-
const AnimatedButton: React.FC<{
|
|
62
|
-
isSelected: boolean;
|
|
63
|
-
onPress: () => void;
|
|
64
|
-
icon: string;
|
|
65
|
-
primaryColor: string;
|
|
66
|
-
textColor: string;
|
|
67
|
-
style: Record<string, unknown>;
|
|
68
|
-
accessibilityLabel: string;
|
|
69
|
-
}> = ({ isSelected, onPress, icon, primaryColor, textColor, style, accessibilityLabel }) => {
|
|
70
|
-
const animatedValue = useRef(new Animated.Value(isSelected ? 1 : 0)).current;
|
|
71
|
-
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
Animated.timing(animatedValue, {
|
|
74
|
-
toValue: isSelected ? 1 : 0,
|
|
75
|
-
duration: 200,
|
|
76
|
-
easing: Easing.out(Easing.ease),
|
|
77
|
-
useNativeDriver: false,
|
|
78
|
-
}).start();
|
|
79
|
-
}, [isSelected, animatedValue]);
|
|
80
|
-
|
|
81
|
-
const backgroundColor = animatedValue.interpolate({
|
|
82
|
-
inputRange: [0, 1],
|
|
83
|
-
outputRange: ['transparent', primaryColor],
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return (
|
|
87
|
-
<TouchableOpacity
|
|
88
|
-
onPress={onPress}
|
|
89
|
-
activeOpacity={0.7}
|
|
90
|
-
accessibilityRole="button"
|
|
91
|
-
accessibilityLabel={accessibilityLabel}
|
|
92
|
-
accessibilityState={{ selected: isSelected }}
|
|
93
|
-
>
|
|
94
|
-
<Animated.View
|
|
95
|
-
style={[
|
|
96
|
-
style,
|
|
97
|
-
{
|
|
98
|
-
backgroundColor,
|
|
99
|
-
},
|
|
100
|
-
]}
|
|
101
|
-
>
|
|
102
|
-
<Animated.View>
|
|
103
|
-
<MaterialCommunityIcons
|
|
104
|
-
name={icon as React.ComponentProps<typeof MaterialCommunityIcons>['name']}
|
|
105
|
-
size={16}
|
|
106
|
-
color={isSelected ? '#FFFFFF' : textColor}
|
|
107
|
-
/>
|
|
108
|
-
</Animated.View>
|
|
109
|
-
</Animated.View>
|
|
110
|
-
</TouchableOpacity>
|
|
111
|
-
);
|
|
112
|
-
};
|
|
113
|
-
|
|
114
62
|
const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
115
63
|
onClose,
|
|
116
64
|
theme,
|
|
@@ -1008,21 +956,12 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1008
956
|
const photoIndex = photos.findIndex(p => p.id === lastSelectedFileId);
|
|
1009
957
|
|
|
1010
958
|
if (photoIndex >= 0) {
|
|
1011
|
-
//
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
else if (safeContainerWidth > 480) itemsPerRow = 4;
|
|
1016
|
-
|
|
1017
|
-
const scrollContainerPadding = 32;
|
|
1018
|
-
const gaps = (itemsPerRow - 1) * 4;
|
|
1019
|
-
const availableWidth = safeContainerWidth - scrollContainerPadding;
|
|
1020
|
-
const itemWidth = (availableWidth - gaps) / itemsPerRow;
|
|
1021
|
-
|
|
1022
|
-
// Calculate row and approximate scroll position
|
|
959
|
+
// Rough scroll estimate for the justified grid (3 photos/row; variable row heights).
|
|
960
|
+
const itemsPerRow = 3;
|
|
961
|
+
const estimatedRowHeight = 150;
|
|
962
|
+
const rowGap = 4;
|
|
1023
963
|
const row = Math.floor(photoIndex / itemsPerRow);
|
|
1024
|
-
const
|
|
1025
|
-
const finalScrollPosition = headerHeight + (row * (itemWidth + 4)) - 150;
|
|
964
|
+
const finalScrollPosition = row * (estimatedRowHeight + rowGap) - 100;
|
|
1026
965
|
|
|
1027
966
|
// Use requestAnimationFrame to ensure DOM is updated before scrolling
|
|
1028
967
|
requestAnimationFrame(() => {
|
|
@@ -1049,45 +988,6 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1049
988
|
}
|
|
1050
989
|
}, [lastSelectedFileId]);
|
|
1051
990
|
|
|
1052
|
-
const renderPhotoItem = (photo: FileMetadata, index: number) => {
|
|
1053
|
-
const downloadUrl = thumbSourceFor(photo);
|
|
1054
|
-
|
|
1055
|
-
// Calculate photo item width based on actual container size from bottom sheet
|
|
1056
|
-
let itemsPerRow = 3; // Default for mobile
|
|
1057
|
-
if (safeContainerWidth > 768) itemsPerRow = 6; // Tablet/Desktop
|
|
1058
|
-
else if (safeContainerWidth > 480) itemsPerRow = 4; // Large mobile
|
|
1059
|
-
|
|
1060
|
-
// Account for the photoScrollContainer padding (16px on each side = 32px total)
|
|
1061
|
-
const scrollContainerPadding = 32; // Total horizontal padding from photoScrollContainer
|
|
1062
|
-
const gaps = (itemsPerRow - 1) * 4; // Gap between items
|
|
1063
|
-
const availableWidth = safeContainerWidth - scrollContainerPadding;
|
|
1064
|
-
const itemWidth = (availableWidth - gaps) / itemsPerRow;
|
|
1065
|
-
|
|
1066
|
-
return (
|
|
1067
|
-
<TouchableOpacity
|
|
1068
|
-
key={photo.id}
|
|
1069
|
-
className="rounded-[8px] overflow-hidden"
|
|
1070
|
-
style={{ width: itemWidth, height: itemWidth }}
|
|
1071
|
-
onPress={() => handleFileOpen(photo)}
|
|
1072
|
-
activeOpacity={0.8}
|
|
1073
|
-
>
|
|
1074
|
-
<View className="w-full h-full relative rounded-[8px] overflow-hidden">
|
|
1075
|
-
<ExpoImage
|
|
1076
|
-
source={{ uri: downloadUrl }}
|
|
1077
|
-
style={screenStyles.photoImage}
|
|
1078
|
-
contentFit="cover"
|
|
1079
|
-
transition={120}
|
|
1080
|
-
cachePolicy="memory-disk"
|
|
1081
|
-
onError={() => {
|
|
1082
|
-
// Image preview failed to load
|
|
1083
|
-
}}
|
|
1084
|
-
accessibilityLabel={photo.filename}
|
|
1085
|
-
/>
|
|
1086
|
-
</View>
|
|
1087
|
-
</TouchableOpacity>
|
|
1088
|
-
);
|
|
1089
|
-
};
|
|
1090
|
-
|
|
1091
991
|
const renderPhotoGrid = useCallback(() => {
|
|
1092
992
|
const photos = filteredFiles.filter(file => file.contentType.startsWith('image/'));
|
|
1093
993
|
|
|
@@ -1159,9 +1059,12 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1159
1059
|
loadPhotoDimensions={loadPhotoDimensions}
|
|
1160
1060
|
createJustifiedRows={createJustifiedRows}
|
|
1161
1061
|
renderJustifiedPhotoItem={renderJustifiedPhotoItem}
|
|
1162
|
-
renderSimplePhotoItem={renderPhotoItem}
|
|
1163
1062
|
textColor={colors.text}
|
|
1164
|
-
containerWidth={
|
|
1063
|
+
containerWidth={
|
|
1064
|
+
typeof containerWidth === 'number' && containerWidth > 0
|
|
1065
|
+
? containerWidth
|
|
1066
|
+
: undefined
|
|
1067
|
+
}
|
|
1165
1068
|
/>
|
|
1166
1069
|
</ScrollView>
|
|
1167
1070
|
);
|
|
@@ -1179,8 +1082,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1179
1082
|
loadPhotoDimensions,
|
|
1180
1083
|
createJustifiedRows,
|
|
1181
1084
|
renderJustifiedPhotoItem,
|
|
1182
|
-
|
|
1183
|
-
safeContainerWidth
|
|
1085
|
+
containerWidth,
|
|
1184
1086
|
]);
|
|
1185
1087
|
|
|
1186
1088
|
// Inline justified grid removed (moved to components/photogrid/JustifiedPhotoGrid.tsx)
|
|
@@ -1230,7 +1132,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1230
1132
|
toValue: 1,
|
|
1231
1133
|
duration: 2000,
|
|
1232
1134
|
easing: Easing.linear,
|
|
1233
|
-
|
|
1135
|
+
// Web has no native animated module; forcing the native
|
|
1136
|
+
// driver there warns and falls back to JS. The shimmer only
|
|
1137
|
+
// drives a transform, so JS-driving it on web is fine.
|
|
1138
|
+
useNativeDriver: Platform.OS !== 'web',
|
|
1234
1139
|
})
|
|
1235
1140
|
);
|
|
1236
1141
|
shimmer.start();
|
|
@@ -151,14 +151,61 @@ export interface PhotoPickerViewProps {
|
|
|
151
151
|
* Apple Photos pattern: when any cell is selected, *non-selected* siblings
|
|
152
152
|
* fade to 0.6 opacity to focus attention on the active selection.
|
|
153
153
|
*/
|
|
154
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Shared cell chrome (image, dim, badge). Ring border is injected by the
|
|
156
|
+
* static vs animated wrappers so web never touches Reanimated worklets.
|
|
157
|
+
*/
|
|
158
|
+
function PhotoPickerCellContent(props: {
|
|
159
|
+
photo: FileMetadata;
|
|
160
|
+
dim: boolean;
|
|
161
|
+
isSelected: boolean;
|
|
162
|
+
selectionIndex: number;
|
|
163
|
+
primaryColor: string;
|
|
164
|
+
thumbUrl: string | undefined;
|
|
165
|
+
ring: React.ReactNode;
|
|
166
|
+
}) {
|
|
167
|
+
const {
|
|
168
|
+
photo, dim, isSelected, selectionIndex, primaryColor, thumbUrl, ring,
|
|
169
|
+
} = props;
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<>
|
|
173
|
+
<View
|
|
174
|
+
className={`flex-1 rounded-radius-8 overflow-hidden bg-[#111111]${dim ? ' opacity-60' : ''}`}
|
|
175
|
+
>
|
|
176
|
+
<ExpoImage
|
|
177
|
+
source={{ uri: thumbUrl }}
|
|
178
|
+
style={{ width: '100%', height: '100%' }}
|
|
179
|
+
contentFit="cover"
|
|
180
|
+
transition={120}
|
|
181
|
+
cachePolicy="memory-disk"
|
|
182
|
+
accessibilityLabel={photo.filename}
|
|
183
|
+
/>
|
|
184
|
+
</View>
|
|
185
|
+
{ring}
|
|
186
|
+
{isSelected && (
|
|
187
|
+
<View
|
|
188
|
+
pointerEvents="none"
|
|
189
|
+
className="absolute top-1.5 right-1.5 min-w-[22px] h-[22px] px-1.5 rounded-full items-center justify-center"
|
|
190
|
+
style={{ backgroundColor: primaryColor }}
|
|
191
|
+
>
|
|
192
|
+
<Text className="text-white text-[12px] font-bold leading-[14px]">
|
|
193
|
+
{selectionIndex > 0 ? String(selectionIndex) : ''}
|
|
194
|
+
</Text>
|
|
195
|
+
</View>
|
|
196
|
+
)}
|
|
197
|
+
</>
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
type PhotoPickerCellProps = {
|
|
155
202
|
photo: FileMetadata;
|
|
156
203
|
size: number;
|
|
157
204
|
marginRight: number;
|
|
158
205
|
marginBottom: number;
|
|
159
206
|
isSelected: boolean;
|
|
160
|
-
selectionIndex: number;
|
|
161
|
-
dim: boolean;
|
|
207
|
+
selectionIndex: number;
|
|
208
|
+
dim: boolean;
|
|
162
209
|
primaryColor: string;
|
|
163
210
|
thumbUrl: string | undefined;
|
|
164
211
|
enterIndex: number;
|
|
@@ -166,21 +213,64 @@ const PhotoPickerCell = React.memo(function PhotoPickerCell(props: {
|
|
|
166
213
|
onPress: () => void;
|
|
167
214
|
onLongPress: () => void;
|
|
168
215
|
a11yLabel: string;
|
|
169
|
-
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const PhotoPickerCellStatic = React.memo(function PhotoPickerCellStatic(props: PhotoPickerCellProps) {
|
|
219
|
+
const {
|
|
220
|
+
photo, size, marginRight, marginBottom, isSelected, selectionIndex,
|
|
221
|
+
dim, primaryColor, thumbUrl, onPress, onLongPress, a11yLabel,
|
|
222
|
+
} = props;
|
|
223
|
+
|
|
224
|
+
const cellWrapperStyle = {
|
|
225
|
+
width: size,
|
|
226
|
+
height: size,
|
|
227
|
+
marginRight,
|
|
228
|
+
marginBottom,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const ring = isSelected ? (
|
|
232
|
+
<View
|
|
233
|
+
pointerEvents="none"
|
|
234
|
+
className="absolute inset-0 rounded-radius-8 border-[3px]"
|
|
235
|
+
style={{ borderColor: primaryColor }}
|
|
236
|
+
/>
|
|
237
|
+
) : null;
|
|
238
|
+
|
|
239
|
+
return (
|
|
240
|
+
<TouchableOpacity
|
|
241
|
+
activeOpacity={0.85}
|
|
242
|
+
onPress={onPress}
|
|
243
|
+
onLongPress={onLongPress}
|
|
244
|
+
className="relative"
|
|
245
|
+
style={cellWrapperStyle}
|
|
246
|
+
accessibilityRole="button"
|
|
247
|
+
accessibilityLabel={a11yLabel}
|
|
248
|
+
accessibilityState={{ selected: isSelected }}
|
|
249
|
+
>
|
|
250
|
+
<PhotoPickerCellContent
|
|
251
|
+
photo={photo}
|
|
252
|
+
dim={dim}
|
|
253
|
+
isSelected={isSelected}
|
|
254
|
+
selectionIndex={selectionIndex}
|
|
255
|
+
primaryColor={primaryColor}
|
|
256
|
+
thumbUrl={thumbUrl}
|
|
257
|
+
ring={ring}
|
|
258
|
+
/>
|
|
259
|
+
</TouchableOpacity>
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const PhotoPickerCellAnimated = React.memo(function PhotoPickerCellAnimated(props: PhotoPickerCellProps) {
|
|
170
264
|
const {
|
|
171
265
|
photo, size, marginRight, marginBottom, isSelected, selectionIndex,
|
|
172
266
|
dim, primaryColor, thumbUrl, enterIndex, reduceMotion, onPress,
|
|
173
267
|
onLongPress, a11yLabel,
|
|
174
268
|
} = props;
|
|
175
269
|
|
|
176
|
-
// Cap the cumulative stagger at ~800ms total so the very long grid does
|
|
177
|
-
// not keep fading in late tiles. Beyond ~53 tiles the delay maxes out.
|
|
178
270
|
const STAGGER_PER_CELL_MS = 15;
|
|
179
271
|
const MAX_TOTAL_STAGGER_MS = 800;
|
|
180
272
|
const delay = Math.min(enterIndex * STAGGER_PER_CELL_MS, MAX_TOTAL_STAGGER_MS);
|
|
181
273
|
|
|
182
|
-
// Selection ring pulse animation: 1.0 → 1.05 → 1.0 on transition to
|
|
183
|
-
// selected. Plays at most once per selection change; reduce-motion skips.
|
|
184
274
|
const ringScale = useSharedValue(1);
|
|
185
275
|
const prevSelected = useRef(isSelected);
|
|
186
276
|
useEffect(() => {
|
|
@@ -204,8 +294,6 @@ const PhotoPickerCell = React.memo(function PhotoPickerCell(props: {
|
|
|
204
294
|
transform: [{ scale: ringScale.value }],
|
|
205
295
|
}));
|
|
206
296
|
|
|
207
|
-
// Per-tile pixel geometry is derived from the measured grid width, so it
|
|
208
|
-
// stays an inline `style` — NativeWind cannot express a runtime pixel size.
|
|
209
297
|
const cellWrapperStyle = {
|
|
210
298
|
width: size,
|
|
211
299
|
height: size,
|
|
@@ -213,44 +301,13 @@ const PhotoPickerCell = React.memo(function PhotoPickerCell(props: {
|
|
|
213
301
|
marginBottom,
|
|
214
302
|
};
|
|
215
303
|
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
// expo-image is a third-party component (no NativeWind
|
|
224
|
-
// className remap), so fill via inline style.
|
|
225
|
-
style={{ width: '100%', height: '100%' }}
|
|
226
|
-
contentFit="cover"
|
|
227
|
-
transition={120}
|
|
228
|
-
cachePolicy="memory-disk"
|
|
229
|
-
accessibilityLabel={photo.filename}
|
|
230
|
-
/>
|
|
231
|
-
</View>
|
|
232
|
-
{isSelected && (
|
|
233
|
-
<Reanimated.View
|
|
234
|
-
pointerEvents="none"
|
|
235
|
-
className="absolute inset-0 rounded-radius-8 border-[3px]"
|
|
236
|
-
// borderColor is the theme primary (dynamic); the animated
|
|
237
|
-
// scale transform must ride on `style` too.
|
|
238
|
-
style={[{ borderColor: primaryColor }, ringAnimatedStyle]}
|
|
239
|
-
/>
|
|
240
|
-
)}
|
|
241
|
-
{isSelected && (
|
|
242
|
-
<View
|
|
243
|
-
pointerEvents="none"
|
|
244
|
-
className="absolute top-1.5 right-1.5 min-w-[22px] h-[22px] px-1.5 rounded-full items-center justify-center"
|
|
245
|
-
style={{ backgroundColor: primaryColor }}
|
|
246
|
-
>
|
|
247
|
-
<Text className="text-white text-[12px] font-bold leading-[14px]">
|
|
248
|
-
{selectionIndex > 0 ? String(selectionIndex) : ''}
|
|
249
|
-
</Text>
|
|
250
|
-
</View>
|
|
251
|
-
)}
|
|
252
|
-
</>
|
|
253
|
-
);
|
|
304
|
+
const ring = isSelected ? (
|
|
305
|
+
<Reanimated.View
|
|
306
|
+
pointerEvents="none"
|
|
307
|
+
className="absolute inset-0 rounded-radius-8 border-[3px]"
|
|
308
|
+
style={[{ borderColor: primaryColor }, ringAnimatedStyle]}
|
|
309
|
+
/>
|
|
310
|
+
) : null;
|
|
254
311
|
|
|
255
312
|
if (reduceMotion) {
|
|
256
313
|
return (
|
|
@@ -264,7 +321,15 @@ const PhotoPickerCell = React.memo(function PhotoPickerCell(props: {
|
|
|
264
321
|
accessibilityLabel={a11yLabel}
|
|
265
322
|
accessibilityState={{ selected: isSelected }}
|
|
266
323
|
>
|
|
267
|
-
|
|
324
|
+
<PhotoPickerCellContent
|
|
325
|
+
photo={photo}
|
|
326
|
+
dim={dim}
|
|
327
|
+
isSelected={isSelected}
|
|
328
|
+
selectionIndex={selectionIndex}
|
|
329
|
+
primaryColor={primaryColor}
|
|
330
|
+
thumbUrl={thumbUrl}
|
|
331
|
+
ring={ring}
|
|
332
|
+
/>
|
|
268
333
|
</TouchableOpacity>
|
|
269
334
|
);
|
|
270
335
|
}
|
|
@@ -284,12 +349,27 @@ const PhotoPickerCell = React.memo(function PhotoPickerCell(props: {
|
|
|
284
349
|
accessibilityLabel={a11yLabel}
|
|
285
350
|
accessibilityState={{ selected: isSelected }}
|
|
286
351
|
>
|
|
287
|
-
|
|
352
|
+
<PhotoPickerCellContent
|
|
353
|
+
photo={photo}
|
|
354
|
+
dim={dim}
|
|
355
|
+
isSelected={isSelected}
|
|
356
|
+
selectionIndex={selectionIndex}
|
|
357
|
+
primaryColor={primaryColor}
|
|
358
|
+
thumbUrl={thumbUrl}
|
|
359
|
+
ring={ring}
|
|
360
|
+
/>
|
|
288
361
|
</TouchableOpacity>
|
|
289
362
|
</Reanimated.View>
|
|
290
363
|
);
|
|
291
364
|
});
|
|
292
365
|
|
|
366
|
+
const PhotoPickerCell = React.memo(function PhotoPickerCell(props: PhotoPickerCellProps) {
|
|
367
|
+
if (Platform.OS === 'web') {
|
|
368
|
+
return <PhotoPickerCellStatic {...props} />;
|
|
369
|
+
}
|
|
370
|
+
return <PhotoPickerCellAnimated {...props} />;
|
|
371
|
+
});
|
|
372
|
+
|
|
293
373
|
const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
294
374
|
photos,
|
|
295
375
|
selectedIds,
|
|
@@ -547,7 +627,8 @@ const PhotoPickerView: React.FC<PhotoPickerViewProps> = ({
|
|
|
547
627
|
</View>
|
|
548
628
|
<View pointerEvents="none">
|
|
549
629
|
<Text
|
|
550
|
-
className="text-white text-[17px] font-semibold text-center
|
|
630
|
+
className="text-white text-[17px] font-semibold text-center"
|
|
631
|
+
style={{ letterSpacing: -0.2 }}
|
|
551
632
|
numberOfLines={1}
|
|
552
633
|
>
|
|
553
634
|
{t('fileManagement.choosePhoto')}
|