@oxyhq/services 22.8.5 → 22.8.6
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/lib/commonjs/ui/components/ProfileButton.js +4 -10
- package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +49 -173
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/components/ProfileButton.js +3 -10
- package/lib/module/ui/components/ProfileButton.js.map +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +50 -174
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ui/components/ProfileButton.tsx +2 -14
- package/src/ui/components/fileManagement/UploadPreview.tsx +1 -1
- package/src/ui/screens/FileManagementScreen.tsx +36 -174
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from 'react-native';
|
|
11
11
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
12
12
|
import { Avatar } from '@oxyhq/bloom/avatar';
|
|
13
|
+
import * as Skeleton from '@oxyhq/bloom/skeleton';
|
|
13
14
|
import { Text } from '@oxyhq/bloom/typography';
|
|
14
15
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
15
16
|
import { getAccountDisplayName, getAccountFallbackHandle } from '@oxyhq/core';
|
|
@@ -140,16 +141,7 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
|
|
|
140
141
|
accessibilityElementsHidden
|
|
141
142
|
importantForAccessibility="no-hide-descendants"
|
|
142
143
|
>
|
|
143
|
-
<
|
|
144
|
-
style={[
|
|
145
|
-
styles.skeletonCircle,
|
|
146
|
-
{
|
|
147
|
-
width: resolvedAvatarSize,
|
|
148
|
-
height: resolvedAvatarSize,
|
|
149
|
-
backgroundColor: colors.backgroundSecondary,
|
|
150
|
-
},
|
|
151
|
-
]}
|
|
152
|
-
/>
|
|
144
|
+
<Skeleton.Circle size={resolvedAvatarSize} />
|
|
153
145
|
</View>
|
|
154
146
|
);
|
|
155
147
|
}
|
|
@@ -352,10 +344,6 @@ const styles = StyleSheet.create({
|
|
|
352
344
|
fullWidth: {
|
|
353
345
|
width: '100%',
|
|
354
346
|
},
|
|
355
|
-
// Neutral skeleton / signed-out avatar circle (`rounded-full`).
|
|
356
|
-
skeletonCircle: {
|
|
357
|
-
borderRadius: 9999,
|
|
358
|
-
},
|
|
359
347
|
// Shared trigger row (`flex-row items-center gap-3 rounded-full px-2 py-2`).
|
|
360
348
|
row: {
|
|
361
349
|
flexDirection: 'row',
|
|
@@ -65,7 +65,7 @@ const UploadPreviewContent: React.FC<{
|
|
|
65
65
|
const isImage = pendingFile.type.startsWith('image/');
|
|
66
66
|
return (
|
|
67
67
|
<View
|
|
68
|
-
key={index}
|
|
68
|
+
key={`${pendingFile.name}-${pendingFile.size}-${index}`}
|
|
69
69
|
className="bg-secondary border-border flex-row items-center p-[12px] rounded-[12px] border mb-[12px] gap-[12px]"
|
|
70
70
|
>
|
|
71
71
|
{isImage && pendingFile.preview ? (
|
|
@@ -9,15 +9,13 @@ import {
|
|
|
9
9
|
RefreshControl,
|
|
10
10
|
TextInput,
|
|
11
11
|
Image,
|
|
12
|
-
Animated,
|
|
13
|
-
Easing,
|
|
14
12
|
AccessibilityInfo,
|
|
15
|
-
Platform,
|
|
16
13
|
useWindowDimensions,
|
|
17
14
|
} from 'react-native';
|
|
18
15
|
import { Image as ExpoImage } from 'expo-image';
|
|
19
16
|
import type { FileManagementScreenProps } from '../types/fileManagement';
|
|
20
17
|
import { Dialog, toast, useDialogControl } from '@oxyhq/bloom';
|
|
18
|
+
import * as Skeleton from '@oxyhq/bloom/skeleton';
|
|
21
19
|
import { Ionicons, MaterialCommunityIcons } from '@expo/vector-icons';
|
|
22
20
|
import type { FileMetadata } from '@oxyhq/core';
|
|
23
21
|
import { useFileStore, useFiles, useUploading as useUploadingStore, useUploadAggregateProgress, useDeleting as useDeletingStore } from '../stores/fileStore';
|
|
@@ -1121,193 +1119,57 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
1121
1119
|
</View>
|
|
1122
1120
|
);
|
|
1123
1121
|
|
|
1124
|
-
//
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
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',
|
|
1139
|
-
})
|
|
1140
|
-
);
|
|
1141
|
-
shimmer.start();
|
|
1142
|
-
return () => shimmer.stop();
|
|
1143
|
-
}, [shimmerAnim]);
|
|
1144
|
-
|
|
1145
|
-
// Create a sweeping shimmer effect
|
|
1146
|
-
const shimmerTranslateX = shimmerAnim.interpolate({
|
|
1147
|
-
inputRange: [0, 1],
|
|
1148
|
-
outputRange: [-skeletonContainerWidth * 2, skeletonContainerWidth * 2],
|
|
1149
|
-
});
|
|
1150
|
-
|
|
1151
|
-
const SkeletonBox = ({ width, height, borderRadius = 8, style, delay = 0 }: { width: number | string; height: number; borderRadius?: number; style?: Record<string, unknown>; delay?: number }) => {
|
|
1152
|
-
const delayedTranslateX = shimmerAnim.interpolate({
|
|
1153
|
-
inputRange: [0, 1],
|
|
1154
|
-
outputRange: [-skeletonContainerWidth * 2 + delay, skeletonContainerWidth * 2 + delay],
|
|
1155
|
-
});
|
|
1156
|
-
|
|
1157
|
-
return (
|
|
1158
|
-
<View
|
|
1159
|
-
style={[
|
|
1160
|
-
{
|
|
1161
|
-
width,
|
|
1162
|
-
height,
|
|
1163
|
-
borderRadius,
|
|
1164
|
-
backgroundColor: colors.backgroundSecondary,
|
|
1165
|
-
overflow: 'hidden',
|
|
1166
|
-
position: 'relative',
|
|
1167
|
-
},
|
|
1168
|
-
style,
|
|
1169
|
-
]}
|
|
1170
|
-
>
|
|
1171
|
-
{/* Base background */}
|
|
1172
|
-
<View
|
|
1173
|
-
style={{
|
|
1174
|
-
position: 'absolute',
|
|
1175
|
-
top: 0,
|
|
1176
|
-
left: 0,
|
|
1177
|
-
right: 0,
|
|
1178
|
-
bottom: 0,
|
|
1179
|
-
backgroundColor: colors.backgroundSecondary,
|
|
1180
|
-
}}
|
|
1181
|
-
/>
|
|
1182
|
-
{/* Shimmer gradient effect */}
|
|
1183
|
-
<Animated.View
|
|
1184
|
-
style={{
|
|
1185
|
-
position: 'absolute',
|
|
1186
|
-
top: 0,
|
|
1187
|
-
left: 0,
|
|
1188
|
-
width: '100%',
|
|
1189
|
-
height: '100%',
|
|
1190
|
-
transform: [{ translateX: delayedTranslateX }],
|
|
1191
|
-
}}
|
|
1192
|
-
>
|
|
1193
|
-
<View
|
|
1194
|
-
style={{
|
|
1195
|
-
width: skeletonContainerWidth,
|
|
1196
|
-
height: '100%',
|
|
1197
|
-
backgroundColor: bloomTheme.isDark
|
|
1198
|
-
? 'rgba(255, 255, 255, 0.08)'
|
|
1199
|
-
: 'rgba(255, 255, 255, 0.8)',
|
|
1200
|
-
shadowColor: bloomTheme.isDark ? '#000' : '#FFF',
|
|
1201
|
-
shadowOffset: { width: 0, height: 0 },
|
|
1202
|
-
shadowOpacity: 0.3,
|
|
1203
|
-
shadowRadius: 10,
|
|
1204
|
-
}}
|
|
1205
|
-
/>
|
|
1206
|
-
</Animated.View>
|
|
1207
|
-
</View>
|
|
1208
|
-
);
|
|
1209
|
-
};
|
|
1210
|
-
|
|
1211
|
-
// Skeleton file item matching SettingsListItem structure
|
|
1212
|
-
const SkeletonFileItem = ({ index }: { index: number }) => (
|
|
1213
|
-
<View
|
|
1214
|
-
style={[
|
|
1215
|
-
{
|
|
1216
|
-
flexDirection: 'row',
|
|
1217
|
-
alignItems: 'center',
|
|
1218
|
-
paddingHorizontal: 16,
|
|
1219
|
-
paddingVertical: 12,
|
|
1220
|
-
backgroundColor: colors.background,
|
|
1221
|
-
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
1222
|
-
borderBottomColor: colors.border,
|
|
1223
|
-
},
|
|
1224
|
-
]}
|
|
1225
|
-
>
|
|
1226
|
-
{/* Icon/Image skeleton */}
|
|
1227
|
-
<SkeletonBox width={44} height={44} borderRadius={8} delay={index * 50} />
|
|
1228
|
-
|
|
1229
|
-
{/* Content skeleton */}
|
|
1230
|
-
<View style={{ flex: 1, marginLeft: 12, justifyContent: 'center' }}>
|
|
1231
|
-
<SkeletonBox
|
|
1232
|
-
width={index % 3 === 0 ? '85%' : index % 3 === 1 ? '70%' : '90%'}
|
|
1233
|
-
height={16}
|
|
1234
|
-
style={{ marginBottom: 8 }}
|
|
1235
|
-
delay={index * 50 + 20}
|
|
1236
|
-
/>
|
|
1237
|
-
<SkeletonBox
|
|
1238
|
-
width={index % 2 === 0 ? '50%' : '60%'}
|
|
1239
|
-
height={12}
|
|
1240
|
-
delay={index * 50 + 40}
|
|
1241
|
-
/>
|
|
1242
|
-
</View>
|
|
1243
|
-
</View>
|
|
1122
|
+
// Loading state — Bloom's shared `Skeleton` primitives own the shimmer, so
|
|
1123
|
+
// this is pure layout: the top chrome (header / controls / search) plus a
|
|
1124
|
+
// photo grid of shimmering tiles sized with the same 3-per-row geometry the
|
|
1125
|
+
// real photo grid uses. No hand-rolled `Animated` shimmer.
|
|
1126
|
+
if (loading) {
|
|
1127
|
+
const GRID_PADDING = 10;
|
|
1128
|
+
const TILE_GAP = 4;
|
|
1129
|
+
const GRID_COLUMNS = 3;
|
|
1130
|
+
const GRID_ROWS = 6;
|
|
1131
|
+
const tileSize = Math.floor(
|
|
1132
|
+
(safeContainerWidth - GRID_PADDING * 2 - TILE_GAP * (GRID_COLUMNS - 1)) / GRID_COLUMNS,
|
|
1244
1133
|
);
|
|
1245
1134
|
|
|
1246
1135
|
return (
|
|
1247
1136
|
<View className="flex-1" style={{ backgroundColor }}>
|
|
1248
|
-
{/* Header
|
|
1137
|
+
{/* Header */}
|
|
1249
1138
|
<View
|
|
1250
|
-
className="flex-row items-center justify-between px-[16px] py-[12px]
|
|
1139
|
+
className="flex-row items-center justify-between px-[16px] py-[12px]"
|
|
1251
1140
|
style={{ borderBottomColor: colors.border, borderBottomWidth: StyleSheet.hairlineWidth }}
|
|
1252
1141
|
>
|
|
1253
|
-
<
|
|
1254
|
-
<
|
|
1255
|
-
<
|
|
1256
|
-
<
|
|
1257
|
-
</
|
|
1258
|
-
<
|
|
1142
|
+
<Skeleton.Box width={44} height={44} borderRadius={12} />
|
|
1143
|
+
<Skeleton.Col style={{ alignItems: 'center', marginHorizontal: 16, gap: 6 }}>
|
|
1144
|
+
<Skeleton.Box width={140} height={20} />
|
|
1145
|
+
<Skeleton.Box width={100} height={14} />
|
|
1146
|
+
</Skeleton.Col>
|
|
1147
|
+
<Skeleton.Box width={44} height={44} borderRadius={12} />
|
|
1259
1148
|
</View>
|
|
1260
1149
|
|
|
1261
|
-
{/* Controls
|
|
1262
|
-
<
|
|
1263
|
-
<
|
|
1264
|
-
<
|
|
1265
|
-
</
|
|
1150
|
+
{/* Controls bar */}
|
|
1151
|
+
<Skeleton.Row style={{ alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 12, paddingVertical: 12 }}>
|
|
1152
|
+
<Skeleton.Box width={100} height={36} borderRadius={18} />
|
|
1153
|
+
<Skeleton.Box width={44} height={44} borderRadius={22} />
|
|
1154
|
+
</Skeleton.Row>
|
|
1266
1155
|
|
|
1267
|
-
{/* Search
|
|
1268
|
-
<View
|
|
1269
|
-
|
|
1270
|
-
style={{ backgroundColor: colors.card }}
|
|
1271
|
-
>
|
|
1272
|
-
<SkeletonBox width="100%" height={44} borderRadius={12} />
|
|
1156
|
+
{/* Search bar */}
|
|
1157
|
+
<View className="mx-[12px] mb-[12px]">
|
|
1158
|
+
<Skeleton.Box width="100%" height={44} borderRadius={22} />
|
|
1273
1159
|
</View>
|
|
1274
1160
|
|
|
1275
|
-
{/*
|
|
1276
|
-
<View
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
<SkeletonBox width={40} height={14} delay={i * 30 + 15} />
|
|
1284
|
-
</View>
|
|
1161
|
+
{/* Photo grid */}
|
|
1162
|
+
<View style={{ padding: GRID_PADDING, gap: TILE_GAP }}>
|
|
1163
|
+
{Array.from({ length: GRID_ROWS }, (_, row) => (
|
|
1164
|
+
<Skeleton.Row key={row} style={{ gap: TILE_GAP }}>
|
|
1165
|
+
{Array.from({ length: GRID_COLUMNS }, (_, col) => (
|
|
1166
|
+
<Skeleton.Box key={col} width={tileSize} height={tileSize} borderRadius={6} />
|
|
1167
|
+
))}
|
|
1168
|
+
</Skeleton.Row>
|
|
1285
1169
|
))}
|
|
1286
1170
|
</View>
|
|
1287
|
-
|
|
1288
|
-
{/* File List Skeleton - Matching SettingsListItem */}
|
|
1289
|
-
<ScrollView
|
|
1290
|
-
className="flex-1"
|
|
1291
|
-
contentContainerClassName="px-[12px] pt-0 pb-[12px]"
|
|
1292
|
-
showsVerticalScrollIndicator={false}
|
|
1293
|
-
>
|
|
1294
|
-
<View style={{
|
|
1295
|
-
backgroundColor: colors.card,
|
|
1296
|
-
borderRadius: 18,
|
|
1297
|
-
overflow: 'hidden',
|
|
1298
|
-
marginTop: 8,
|
|
1299
|
-
}}>
|
|
1300
|
-
{[1, 2, 3, 4, 5, 6, 7, 8].map((i) => (
|
|
1301
|
-
<SkeletonFileItem key={i} index={i} />
|
|
1302
|
-
))}
|
|
1303
|
-
</View>
|
|
1304
|
-
</ScrollView>
|
|
1305
1171
|
</View>
|
|
1306
1172
|
);
|
|
1307
|
-
});
|
|
1308
|
-
|
|
1309
|
-
if (loading) {
|
|
1310
|
-
return <SkeletonLoader />;
|
|
1311
1173
|
}
|
|
1312
1174
|
|
|
1313
1175
|
// Dedicated flagship-style photo picker view used in the avatar-picker
|