react-native-inapp-inspector 2.3.18 → 2.3.19
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/com/inappinspector/NetworkInspectorModule.kt +47 -0
- package/dist/commonjs/capture.d.ts +2 -0
- package/dist/commonjs/capture.js +3 -0
- package/dist/commonjs/components/EmptyState.d.ts +1 -0
- package/dist/commonjs/components/EmptyState.js +2 -2
- package/dist/commonjs/components/Inspector/FabLauncher.js +494 -39
- package/dist/commonjs/components/Inspector/InspectorHeader.js +45 -26
- package/dist/commonjs/components/Inspector/MainScreen.js +4 -3
- package/dist/commonjs/components/Inspector/MediaGalleryTab.js +418 -13
- package/dist/commonjs/components/Inspector/MediaPreviewModal.js +376 -88
- package/dist/commonjs/components/Inspector/NetworkDetail.js +49 -63
- package/dist/commonjs/components/Inspector/SettingsPanel.js +499 -119
- package/dist/commonjs/components/Inspector/TabBar.js +166 -4
- package/dist/commonjs/components/JsonViewer.js +4 -3
- package/dist/commonjs/components/NetworkIcons.d.ts +4 -0
- package/dist/commonjs/components/NetworkIcons.js +26 -1
- package/dist/commonjs/components/SegmentedTabs.js +1 -0
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/i18n/locales/en.json +20 -2
- package/dist/commonjs/index.js +45 -30
- package/dist/commonjs/native/NativeInspector.d.ts +3 -0
- package/dist/commonjs/native/NativeInspector.js +14 -1
- package/dist/commonjs/native/NativeNetworkInspector.d.ts +1 -0
- package/dist/commonjs/styles/AppColors.js +78 -78
- package/dist/commonjs/types/enums.d.ts +2 -0
- package/dist/commonjs/types/enums.js +2 -0
- package/dist/commonjs/types/interfaces.d.ts +5 -0
- package/dist/esm/capture.d.ts +2 -0
- package/dist/esm/capture.js +4 -1
- package/dist/esm/components/EmptyState.d.ts +1 -0
- package/dist/esm/components/EmptyState.js +2 -2
- package/dist/esm/components/Inspector/FabLauncher.js +462 -40
- package/dist/esm/components/Inspector/InspectorHeader.js +46 -27
- package/dist/esm/components/Inspector/MainScreen.js +4 -3
- package/dist/esm/components/Inspector/MediaGalleryTab.js +419 -14
- package/dist/esm/components/Inspector/MediaPreviewModal.js +379 -91
- package/dist/esm/components/Inspector/NetworkDetail.js +49 -63
- package/dist/esm/components/Inspector/SettingsPanel.js +500 -120
- package/dist/esm/components/Inspector/TabBar.js +135 -6
- package/dist/esm/components/JsonViewer.js +4 -3
- package/dist/esm/components/NetworkIcons.d.ts +4 -0
- package/dist/esm/components/NetworkIcons.js +21 -0
- package/dist/esm/components/SegmentedTabs.js +1 -0
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/i18n/locales/en.json +20 -2
- package/dist/esm/index.js +44 -29
- package/dist/esm/native/NativeInspector.d.ts +3 -0
- package/dist/esm/native/NativeInspector.js +12 -0
- package/dist/esm/native/NativeNetworkInspector.d.ts +1 -0
- package/dist/esm/styles/AppColors.js +78 -78
- package/dist/esm/types/enums.d.ts +2 -0
- package/dist/esm/types/enums.js +2 -0
- package/dist/esm/types/interfaces.d.ts +5 -0
- package/ios/NetworkInspectorModule.mm +241 -136
- package/package.json +1 -1
- package/react-native-inapp-inspector.podspec +1 -1
- package/src/capture.ts +8 -0
- package/src/components/EmptyState.tsx +3 -1
- package/src/components/Inspector/FabLauncher.tsx +562 -52
- package/src/components/Inspector/InspectorHeader.tsx +60 -22
- package/src/components/Inspector/MainScreen.tsx +3 -2
- package/src/components/Inspector/MediaGalleryTab.tsx +502 -28
- package/src/components/Inspector/MediaPreviewModal.tsx +465 -117
- package/src/components/Inspector/NetworkDetail.tsx +80 -97
- package/src/components/Inspector/SettingsPanel.tsx +708 -152
- package/src/components/Inspector/TabBar.tsx +174 -6
- package/src/components/JsonViewer.tsx +4 -2
- package/src/components/NetworkIcons.tsx +56 -0
- package/src/components/SegmentedTabs.tsx +1 -0
- package/src/constants/version.ts +1 -1
- package/src/i18n/locales/en.json +20 -4
- package/src/index.tsx +46 -31
- package/src/native/NativeInspector.ts +17 -0
- package/src/native/NativeNetworkInspector.ts +1 -0
- package/src/styles/AppColors.ts +80 -80
- package/src/types/enums.ts +2 -0
- package/src/types/interfaces.ts +5 -0
|
@@ -15,13 +15,15 @@ import {AppFonts} from '../../styles/AppFonts';
|
|
|
15
15
|
import TouchableScale from '../TouchableScale';
|
|
16
16
|
import EmptyState from '../EmptyState';
|
|
17
17
|
import {
|
|
18
|
-
|
|
18
|
+
CheckIcon,
|
|
19
19
|
FilmIcon,
|
|
20
20
|
GifIcon,
|
|
21
|
+
GridIcon,
|
|
21
22
|
ImageIcon,
|
|
22
|
-
|
|
23
|
+
ListIcon,
|
|
24
|
+
PlayIcon,
|
|
25
|
+
ScreencastIcon,
|
|
23
26
|
TrashIcon,
|
|
24
|
-
VideoCameraIcon,
|
|
25
27
|
} from '../NetworkIcons';
|
|
26
28
|
import {MediaPreviewModal} from './MediaPreviewModal';
|
|
27
29
|
import {
|
|
@@ -32,21 +34,27 @@ import {formatBytes} from '../../helpers';
|
|
|
32
34
|
import {showToast} from '../../helpers/toast';
|
|
33
35
|
import {useTranslation} from '../../i18n';
|
|
34
36
|
|
|
37
|
+
import {useInspector} from './InspectorContext';
|
|
38
|
+
|
|
35
39
|
export const MediaGalleryTab: React.FC = () => {
|
|
36
40
|
const {t} = useTranslation();
|
|
41
|
+
const {refreshMediaCount, switchActiveTab} = useInspector();
|
|
37
42
|
const [mediaList, setMediaList] = useState<CapturedMediaItem[]>([]);
|
|
38
43
|
const [refreshing, setRefreshing] = useState(false);
|
|
39
44
|
const [selectedFilter, setSelectedFilter] = useState<'all' | 'image' | 'video' | 'gif'>('all');
|
|
40
45
|
const [selectedItem, setSelectedItem] = useState<CapturedMediaItem | null>(null);
|
|
46
|
+
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
|
47
|
+
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
|
|
41
48
|
|
|
42
49
|
const loadMedia = useCallback(async () => {
|
|
43
50
|
try {
|
|
44
51
|
const items = await ScreenCapture.getMediaList();
|
|
45
52
|
setMediaList(items);
|
|
53
|
+
refreshMediaCount?.().catch(() => {});
|
|
46
54
|
} catch {
|
|
47
55
|
setMediaList([]);
|
|
48
56
|
}
|
|
49
|
-
}, []);
|
|
57
|
+
}, [refreshMediaCount]);
|
|
50
58
|
|
|
51
59
|
useEffect(() => {
|
|
52
60
|
loadMedia();
|
|
@@ -67,12 +75,63 @@ export const MediaGalleryTab: React.FC = () => {
|
|
|
67
75
|
return mediaList.reduce((sum, item) => sum + (item.sizeBytes || 0), 0);
|
|
68
76
|
}, [mediaList]);
|
|
69
77
|
|
|
78
|
+
const toggleSelect = useCallback((id: string) => {
|
|
79
|
+
setSelectedIds(prev => {
|
|
80
|
+
const next = new Set(prev);
|
|
81
|
+
if (next.has(id)) {
|
|
82
|
+
next.delete(id);
|
|
83
|
+
} else {
|
|
84
|
+
next.add(id);
|
|
85
|
+
}
|
|
86
|
+
return next;
|
|
87
|
+
});
|
|
88
|
+
}, []);
|
|
89
|
+
|
|
70
90
|
const handleDeleteItem = async (item: CapturedMediaItem) => {
|
|
71
91
|
await ScreenCapture.deleteMedia(item.uri);
|
|
92
|
+
setSelectedIds(prev => {
|
|
93
|
+
const next = new Set(prev);
|
|
94
|
+
next.delete(item.id);
|
|
95
|
+
return next;
|
|
96
|
+
});
|
|
72
97
|
await loadMedia();
|
|
98
|
+
const count = await refreshMediaCount?.();
|
|
99
|
+
if (count === 0) {
|
|
100
|
+
switchActiveTab('apis');
|
|
101
|
+
}
|
|
73
102
|
showToast(t('mediaGallery.deleted'));
|
|
74
103
|
};
|
|
75
104
|
|
|
105
|
+
const handleDeleteSelected = () => {
|
|
106
|
+
const count = selectedIds.size;
|
|
107
|
+
if (count === 0) return;
|
|
108
|
+
|
|
109
|
+
Alert.alert(
|
|
110
|
+
t('mediaGallery.deleteSelectedTitle'),
|
|
111
|
+
t('mediaGallery.deleteSelectedMessage', {count}),
|
|
112
|
+
[
|
|
113
|
+
{text: t('common.cancel'), style: 'cancel'},
|
|
114
|
+
{
|
|
115
|
+
text: t('mediaGallery.delete'),
|
|
116
|
+
style: 'destructive',
|
|
117
|
+
onPress: async () => {
|
|
118
|
+
const itemsToDelete = mediaList.filter(item => selectedIds.has(item.id));
|
|
119
|
+
for (const item of itemsToDelete) {
|
|
120
|
+
await ScreenCapture.deleteMedia(item.uri);
|
|
121
|
+
}
|
|
122
|
+
setSelectedIds(new Set());
|
|
123
|
+
await loadMedia();
|
|
124
|
+
const remainingCount = await refreshMediaCount?.();
|
|
125
|
+
if (remainingCount === 0) {
|
|
126
|
+
switchActiveTab('apis');
|
|
127
|
+
}
|
|
128
|
+
showToast(t('mediaGallery.deletedCount', {count: itemsToDelete.length}));
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
76
135
|
const handleClearAll = () => {
|
|
77
136
|
Alert.alert(
|
|
78
137
|
t('mediaGallery.purgeTitle'),
|
|
@@ -84,7 +143,10 @@ export const MediaGalleryTab: React.FC = () => {
|
|
|
84
143
|
style: 'destructive',
|
|
85
144
|
onPress: async () => {
|
|
86
145
|
await ScreenCapture.clearAllMedia();
|
|
146
|
+
setSelectedIds(new Set());
|
|
87
147
|
await loadMedia();
|
|
148
|
+
await refreshMediaCount?.();
|
|
149
|
+
switchActiveTab('apis');
|
|
88
150
|
showToast(t('mediaGallery.allPurged'));
|
|
89
151
|
},
|
|
90
152
|
},
|
|
@@ -96,23 +158,69 @@ export const MediaGalleryTab: React.FC = () => {
|
|
|
96
158
|
const gifResult = await ScreenCapture.convertToGif(item.uri, {fps: 12, width: 480});
|
|
97
159
|
if (gifResult) {
|
|
98
160
|
await loadMedia();
|
|
161
|
+
refreshMediaCount?.().catch(() => {});
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const renderFilterIcon = (filterKey: 'all' | 'image' | 'video' | 'gif', isActive: boolean) => {
|
|
166
|
+
const iconColor = isActive ? AppColors.sky400 : AppColors.slate400;
|
|
167
|
+
const iconSize = 12;
|
|
168
|
+
|
|
169
|
+
switch (filterKey) {
|
|
170
|
+
case 'all':
|
|
171
|
+
return <ScreencastIcon size={iconSize} color={iconColor} />;
|
|
172
|
+
case 'image':
|
|
173
|
+
return <ImageIcon size={iconSize} color={iconColor} />;
|
|
174
|
+
case 'video':
|
|
175
|
+
return <FilmIcon size={iconSize} color={iconColor} />;
|
|
176
|
+
case 'gif':
|
|
177
|
+
return <GifIcon size={iconSize} color={iconColor} />;
|
|
99
178
|
}
|
|
100
179
|
};
|
|
101
180
|
|
|
102
|
-
const
|
|
181
|
+
const renderGridItem = ({item}: {item: CapturedMediaItem}) => {
|
|
103
182
|
const isVideo = item.type === 'video';
|
|
104
183
|
const isGif = item.type === 'gif';
|
|
184
|
+
const thumbUri = isVideo ? item.thumbnailUri : item.uri;
|
|
185
|
+
const isSelected = selectedIds.has(item.id);
|
|
105
186
|
|
|
106
187
|
return (
|
|
107
188
|
<TouchableScale
|
|
108
189
|
onPress={() => setSelectedItem(item)}
|
|
109
|
-
style={galleryStyles.card}>
|
|
190
|
+
style={[galleryStyles.card, isSelected && galleryStyles.cardSelected]}>
|
|
110
191
|
<View style={galleryStyles.thumbnailContainer}>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
192
|
+
{thumbUri ? (
|
|
193
|
+
<Image
|
|
194
|
+
source={{uri: thumbUri}}
|
|
195
|
+
style={galleryStyles.thumbnail}
|
|
196
|
+
resizeMode="cover"
|
|
197
|
+
/>
|
|
198
|
+
) : (
|
|
199
|
+
<View style={galleryStyles.thumbnailFallback}>
|
|
200
|
+
{isVideo ? (
|
|
201
|
+
<FilmIcon size={26} color={AppColors.sky400} />
|
|
202
|
+
) : (
|
|
203
|
+
<ImageIcon size={26} color={AppColors.slate400} />
|
|
204
|
+
)}
|
|
205
|
+
</View>
|
|
206
|
+
)}
|
|
207
|
+
|
|
208
|
+
{/* Checkbox top-left */}
|
|
209
|
+
<TouchableOpacity
|
|
210
|
+
onPress={() => toggleSelect(item.id)}
|
|
211
|
+
style={[
|
|
212
|
+
galleryStyles.checkbox,
|
|
213
|
+
isSelected && galleryStyles.checkboxSelected,
|
|
214
|
+
]}
|
|
215
|
+
hitSlop={{top: 8, bottom: 8, left: 8, right: 8}}>
|
|
216
|
+
{isSelected && <CheckIcon size={12} color={AppColors.white} />}
|
|
217
|
+
</TouchableOpacity>
|
|
218
|
+
|
|
219
|
+
{isVideo && (
|
|
220
|
+
<View style={galleryStyles.miniPlayCircle}>
|
|
221
|
+
<PlayIcon size={14} color={AppColors.white} />
|
|
222
|
+
</View>
|
|
223
|
+
)}
|
|
116
224
|
<View style={galleryStyles.typeBadge}>
|
|
117
225
|
{isVideo ? (
|
|
118
226
|
<FilmIcon size={11} color={AppColors.sky400} />
|
|
@@ -145,9 +253,94 @@ export const MediaGalleryTab: React.FC = () => {
|
|
|
145
253
|
);
|
|
146
254
|
};
|
|
147
255
|
|
|
256
|
+
const renderListItem = ({item}: {item: CapturedMediaItem}) => {
|
|
257
|
+
const isVideo = item.type === 'video';
|
|
258
|
+
const isGif = item.type === 'gif';
|
|
259
|
+
const thumbUri = isVideo ? item.thumbnailUri : item.uri;
|
|
260
|
+
const isSelected = selectedIds.has(item.id);
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<TouchableScale
|
|
264
|
+
onPress={() => setSelectedItem(item)}
|
|
265
|
+
style={[galleryStyles.listRow, isSelected && galleryStyles.listRowSelected]}>
|
|
266
|
+
{/* Checkbox */}
|
|
267
|
+
<TouchableOpacity
|
|
268
|
+
onPress={() => toggleSelect(item.id)}
|
|
269
|
+
style={[
|
|
270
|
+
galleryStyles.listCheckbox,
|
|
271
|
+
isSelected && galleryStyles.checkboxSelected,
|
|
272
|
+
]}
|
|
273
|
+
hitSlop={{top: 8, bottom: 8, left: 8, right: 8}}>
|
|
274
|
+
{isSelected && <CheckIcon size={12} color={AppColors.white} />}
|
|
275
|
+
</TouchableOpacity>
|
|
276
|
+
|
|
277
|
+
{/* Square Thumbnail */}
|
|
278
|
+
<View style={galleryStyles.listThumbContainer}>
|
|
279
|
+
{thumbUri ? (
|
|
280
|
+
<Image
|
|
281
|
+
source={{uri: thumbUri}}
|
|
282
|
+
style={galleryStyles.listThumb}
|
|
283
|
+
resizeMode="cover"
|
|
284
|
+
/>
|
|
285
|
+
) : (
|
|
286
|
+
<View style={galleryStyles.listThumbFallback}>
|
|
287
|
+
{isVideo ? (
|
|
288
|
+
<FilmIcon size={18} color={AppColors.sky400} />
|
|
289
|
+
) : (
|
|
290
|
+
<ImageIcon size={18} color={AppColors.slate400} />
|
|
291
|
+
)}
|
|
292
|
+
</View>
|
|
293
|
+
)}
|
|
294
|
+
{isVideo && (
|
|
295
|
+
<View style={galleryStyles.listMiniPlay}>
|
|
296
|
+
<PlayIcon size={10} color={AppColors.white} />
|
|
297
|
+
</View>
|
|
298
|
+
)}
|
|
299
|
+
</View>
|
|
300
|
+
|
|
301
|
+
{/* Text Metadata */}
|
|
302
|
+
<View style={galleryStyles.listInfo}>
|
|
303
|
+
<Text style={galleryStyles.listTitle} numberOfLines={1}>
|
|
304
|
+
{item.filename}
|
|
305
|
+
</Text>
|
|
306
|
+
<View style={galleryStyles.listMetaRow}>
|
|
307
|
+
<View style={galleryStyles.listBadge}>
|
|
308
|
+
{isVideo ? (
|
|
309
|
+
<FilmIcon size={10} color={AppColors.sky400} />
|
|
310
|
+
) : isGif ? (
|
|
311
|
+
<GifIcon size={10} color={AppColors.warningAmber} />
|
|
312
|
+
) : (
|
|
313
|
+
<ImageIcon size={10} color={AppColors.emerald500} />
|
|
314
|
+
)}
|
|
315
|
+
<Text style={galleryStyles.listBadgeText}>
|
|
316
|
+
{item.format.toUpperCase()}
|
|
317
|
+
</Text>
|
|
318
|
+
</View>
|
|
319
|
+
<Text style={galleryStyles.cardMeta}>
|
|
320
|
+
{formatBytes(item.sizeBytes)}
|
|
321
|
+
</Text>
|
|
322
|
+
<Text style={galleryStyles.cardMetaDot}>•</Text>
|
|
323
|
+
<Text style={galleryStyles.cardMeta}>
|
|
324
|
+
{new Date(item.timestamp).toLocaleDateString([], {month: 'short', day: 'numeric'})}{' '}
|
|
325
|
+
{new Date(item.timestamp).toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'})}
|
|
326
|
+
</Text>
|
|
327
|
+
</View>
|
|
328
|
+
</View>
|
|
329
|
+
|
|
330
|
+
{/* Delete button */}
|
|
331
|
+
<TouchableOpacity
|
|
332
|
+
onPress={() => handleDeleteItem(item)}
|
|
333
|
+
style={galleryStyles.listDeleteBtn}
|
|
334
|
+
hitSlop={{top: 8, bottom: 8, left: 8, right: 8}}>
|
|
335
|
+
<TrashIcon size={14} color={AppColors.red500} />
|
|
336
|
+
</TouchableOpacity>
|
|
337
|
+
</TouchableScale>
|
|
338
|
+
);
|
|
339
|
+
};
|
|
340
|
+
|
|
148
341
|
return (
|
|
149
342
|
<View style={galleryStyles.container}>
|
|
150
|
-
{/* Top Bar with Filter Chips
|
|
343
|
+
{/* Top Bar with Filter Chips on Left & Grid/List Toggle on Right */}
|
|
151
344
|
<View style={galleryStyles.filterBar}>
|
|
152
345
|
<View style={galleryStyles.chipGroup}>
|
|
153
346
|
{(['all', 'image', 'video', 'gif'] as const).map(filterKey => {
|
|
@@ -169,6 +362,7 @@ export const MediaGalleryTab: React.FC = () => {
|
|
|
169
362
|
galleryStyles.chip,
|
|
170
363
|
isActive && galleryStyles.chipActive,
|
|
171
364
|
]}>
|
|
365
|
+
{renderFilterIcon(filterKey, isActive)}
|
|
172
366
|
<Text
|
|
173
367
|
style={[
|
|
174
368
|
galleryStyles.chipText,
|
|
@@ -181,24 +375,89 @@ export const MediaGalleryTab: React.FC = () => {
|
|
|
181
375
|
})}
|
|
182
376
|
</View>
|
|
183
377
|
|
|
184
|
-
{
|
|
378
|
+
{/* View Mode Toggle Button Group */}
|
|
379
|
+
<View style={galleryStyles.viewToggleContainer}>
|
|
185
380
|
<TouchableOpacity
|
|
186
|
-
onPress={
|
|
187
|
-
style={
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
381
|
+
onPress={() => setViewMode('grid')}
|
|
382
|
+
style={[
|
|
383
|
+
galleryStyles.viewToggleBtn,
|
|
384
|
+
viewMode === 'grid' && galleryStyles.viewToggleBtnActive,
|
|
385
|
+
]}>
|
|
386
|
+
<GridIcon size={13} color={viewMode === 'grid' ? AppColors.white : AppColors.slate400} />
|
|
387
|
+
</TouchableOpacity>
|
|
388
|
+
<TouchableOpacity
|
|
389
|
+
onPress={() => setViewMode('list')}
|
|
390
|
+
style={[
|
|
391
|
+
galleryStyles.viewToggleBtn,
|
|
392
|
+
viewMode === 'list' && galleryStyles.viewToggleBtnActive,
|
|
393
|
+
]}>
|
|
394
|
+
<ListIcon size={13} color={viewMode === 'list' ? AppColors.white : AppColors.slate400} />
|
|
191
395
|
</TouchableOpacity>
|
|
192
|
-
|
|
396
|
+
</View>
|
|
193
397
|
</View>
|
|
194
398
|
|
|
195
|
-
{/*
|
|
399
|
+
{/* Sub Bar with Results Counter on Left & Actions on Right */}
|
|
400
|
+
{mediaList.length > 0 && (
|
|
401
|
+
<View style={galleryStyles.subBar}>
|
|
402
|
+
<View style={galleryStyles.resultCountWrapper}>
|
|
403
|
+
<Text
|
|
404
|
+
style={[
|
|
405
|
+
galleryStyles.resultCountText,
|
|
406
|
+
selectedIds.size > 0 && galleryStyles.resultCountTextSelected,
|
|
407
|
+
]}>
|
|
408
|
+
{selectedIds.size > 0
|
|
409
|
+
? t('mediaGallery.selectedOfResults', {
|
|
410
|
+
selected: selectedIds.size,
|
|
411
|
+
total: filteredMedia.length,
|
|
412
|
+
defaultValue: `Selected ${selectedIds.size} of ${filteredMedia.length} results`,
|
|
413
|
+
})
|
|
414
|
+
: filteredMedia.length !== mediaList.length
|
|
415
|
+
? t('mediaGallery.showingFilteredResults', {
|
|
416
|
+
count: filteredMedia.length,
|
|
417
|
+
total: mediaList.length,
|
|
418
|
+
defaultValue: `Showing ${filteredMedia.length} of ${mediaList.length} results`,
|
|
419
|
+
})
|
|
420
|
+
: t('mediaGallery.showingResults', {
|
|
421
|
+
count: filteredMedia.length,
|
|
422
|
+
defaultValue: `Showing ${filteredMedia.length} results`,
|
|
423
|
+
})}
|
|
424
|
+
</Text>
|
|
425
|
+
</View>
|
|
426
|
+
|
|
427
|
+
<View style={galleryStyles.actionsGroup}>
|
|
428
|
+
{selectedIds.size > 0 && (
|
|
429
|
+
<TouchableOpacity
|
|
430
|
+
onPress={handleDeleteSelected}
|
|
431
|
+
style={galleryStyles.deleteBtn}
|
|
432
|
+
hitSlop={{top: 8, bottom: 8, left: 8, right: 8}}>
|
|
433
|
+
<TrashIcon size={13} color={AppColors.red500} />
|
|
434
|
+
<Text style={galleryStyles.deleteBtnText}>
|
|
435
|
+
{t('mediaGallery.deleteSelected', {count: selectedIds.size})}
|
|
436
|
+
</Text>
|
|
437
|
+
</TouchableOpacity>
|
|
438
|
+
)}
|
|
439
|
+
|
|
440
|
+
<TouchableOpacity
|
|
441
|
+
onPress={handleClearAll}
|
|
442
|
+
style={galleryStyles.clearBtn}
|
|
443
|
+
hitSlop={{top: 8, bottom: 8, left: 8, right: 8}}>
|
|
444
|
+
<TrashIcon size={13} color={AppColors.red500} />
|
|
445
|
+
<Text style={galleryStyles.clearBtnText}>
|
|
446
|
+
{t('mediaGallery.purge', {size: formatBytes(totalStorageBytes)})}
|
|
447
|
+
</Text>
|
|
448
|
+
</TouchableOpacity>
|
|
449
|
+
</View>
|
|
450
|
+
</View>
|
|
451
|
+
)}
|
|
452
|
+
|
|
453
|
+
{/* Media Items (Grid or List) */}
|
|
196
454
|
<FlatList
|
|
455
|
+
key={viewMode}
|
|
197
456
|
data={filteredMedia}
|
|
198
457
|
keyExtractor={item => item.id}
|
|
199
|
-
renderItem={
|
|
200
|
-
numColumns={2}
|
|
201
|
-
columnWrapperStyle={galleryStyles.columnWrapper}
|
|
458
|
+
renderItem={viewMode === 'grid' ? renderGridItem : renderListItem}
|
|
459
|
+
numColumns={viewMode === 'grid' ? 2 : 1}
|
|
460
|
+
columnWrapperStyle={viewMode === 'grid' ? galleryStyles.columnWrapper : undefined}
|
|
202
461
|
contentContainerStyle={galleryStyles.listContent}
|
|
203
462
|
refreshControl={
|
|
204
463
|
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={AppColors.sky400} />
|
|
@@ -207,11 +466,11 @@ export const MediaGalleryTab: React.FC = () => {
|
|
|
207
466
|
<EmptyState
|
|
208
467
|
customTitle={t('mediaGallery.noMediaTitle')}
|
|
209
468
|
customSub={t('mediaGallery.noMediaDesc')}
|
|
469
|
+
showReload={false}
|
|
210
470
|
/>
|
|
211
471
|
}
|
|
212
472
|
/>
|
|
213
473
|
|
|
214
|
-
|
|
215
474
|
{/* Fullscreen Preview Modal */}
|
|
216
475
|
<MediaPreviewModal
|
|
217
476
|
item={selectedItem}
|
|
@@ -237,19 +496,22 @@ const galleryStyles = StyleSheet.create({
|
|
|
237
496
|
alignItems: 'center',
|
|
238
497
|
justifyContent: 'space-between',
|
|
239
498
|
paddingHorizontal: 12,
|
|
240
|
-
paddingVertical:
|
|
499
|
+
paddingVertical: 9,
|
|
241
500
|
borderBottomWidth: 1,
|
|
242
501
|
borderBottomColor: AppColors.borderGlassLight,
|
|
243
|
-
flexWrap: 'wrap',
|
|
244
502
|
gap: 8,
|
|
245
503
|
},
|
|
246
504
|
chipGroup: {
|
|
247
505
|
flexDirection: 'row',
|
|
248
506
|
alignItems: 'center',
|
|
249
507
|
gap: 6,
|
|
508
|
+
flexShrink: 1,
|
|
250
509
|
},
|
|
251
510
|
chip: {
|
|
252
|
-
|
|
511
|
+
flexDirection: 'row',
|
|
512
|
+
alignItems: 'center',
|
|
513
|
+
gap: 4.5,
|
|
514
|
+
paddingHorizontal: 8,
|
|
253
515
|
paddingVertical: 4.5,
|
|
254
516
|
borderRadius: 6,
|
|
255
517
|
backgroundColor: AppColors.whiteAlpha06,
|
|
@@ -269,6 +531,71 @@ const galleryStyles = StyleSheet.create({
|
|
|
269
531
|
fontFamily: AppFonts.interBold,
|
|
270
532
|
color: AppColors.sky400,
|
|
271
533
|
},
|
|
534
|
+
subBar: {
|
|
535
|
+
flexDirection: 'row',
|
|
536
|
+
alignItems: 'center',
|
|
537
|
+
justifyContent: 'space-between',
|
|
538
|
+
paddingHorizontal: 12,
|
|
539
|
+
paddingVertical: 7,
|
|
540
|
+
backgroundColor: `${AppColors.slate900}99`,
|
|
541
|
+
borderBottomWidth: 1,
|
|
542
|
+
borderBottomColor: AppColors.borderGlassLight,
|
|
543
|
+
gap: 8,
|
|
544
|
+
},
|
|
545
|
+
resultCountWrapper: {
|
|
546
|
+
flexDirection: 'row',
|
|
547
|
+
alignItems: 'center',
|
|
548
|
+
flexShrink: 1,
|
|
549
|
+
},
|
|
550
|
+
resultCountText: {
|
|
551
|
+
fontFamily: AppFonts.interMedium,
|
|
552
|
+
fontSize: 11,
|
|
553
|
+
color: AppColors.slate400,
|
|
554
|
+
},
|
|
555
|
+
resultCountTextSelected: {
|
|
556
|
+
fontFamily: AppFonts.interSemiBold,
|
|
557
|
+
color: AppColors.sky400,
|
|
558
|
+
},
|
|
559
|
+
actionsGroup: {
|
|
560
|
+
flexDirection: 'row',
|
|
561
|
+
alignItems: 'center',
|
|
562
|
+
gap: 6,
|
|
563
|
+
},
|
|
564
|
+
viewToggleContainer: {
|
|
565
|
+
flexDirection: 'row',
|
|
566
|
+
alignItems: 'center',
|
|
567
|
+
backgroundColor: AppColors.whiteAlpha06,
|
|
568
|
+
borderRadius: 6,
|
|
569
|
+
padding: 2,
|
|
570
|
+
borderWidth: 1,
|
|
571
|
+
borderColor: AppColors.whiteAlpha10,
|
|
572
|
+
},
|
|
573
|
+
viewToggleBtn: {
|
|
574
|
+
paddingHorizontal: 7,
|
|
575
|
+
paddingVertical: 4,
|
|
576
|
+
borderRadius: 4,
|
|
577
|
+
alignItems: 'center',
|
|
578
|
+
justifyContent: 'center',
|
|
579
|
+
},
|
|
580
|
+
viewToggleBtnActive: {
|
|
581
|
+
backgroundColor: `${AppColors.violet600}80`,
|
|
582
|
+
},
|
|
583
|
+
deleteBtn: {
|
|
584
|
+
flexDirection: 'row',
|
|
585
|
+
alignItems: 'center',
|
|
586
|
+
gap: 4,
|
|
587
|
+
paddingHorizontal: 8,
|
|
588
|
+
paddingVertical: 4,
|
|
589
|
+
borderRadius: 6,
|
|
590
|
+
backgroundColor: `${AppColors.red500}26`,
|
|
591
|
+
borderWidth: 1,
|
|
592
|
+
borderColor: `${AppColors.red500}80`,
|
|
593
|
+
},
|
|
594
|
+
deleteBtnText: {
|
|
595
|
+
fontFamily: AppFonts.interBold,
|
|
596
|
+
fontSize: 10.5,
|
|
597
|
+
color: AppColors.red500,
|
|
598
|
+
},
|
|
272
599
|
clearBtn: {
|
|
273
600
|
flexDirection: 'row',
|
|
274
601
|
alignItems: 'center',
|
|
@@ -287,7 +614,7 @@ const galleryStyles = StyleSheet.create({
|
|
|
287
614
|
},
|
|
288
615
|
listContent: {
|
|
289
616
|
padding: 12,
|
|
290
|
-
gap:
|
|
617
|
+
gap: 10,
|
|
291
618
|
},
|
|
292
619
|
columnWrapper: {
|
|
293
620
|
gap: 12,
|
|
@@ -300,6 +627,10 @@ const galleryStyles = StyleSheet.create({
|
|
|
300
627
|
borderColor: AppColors.borderGlassLight,
|
|
301
628
|
overflow: 'hidden',
|
|
302
629
|
},
|
|
630
|
+
cardSelected: {
|
|
631
|
+
borderColor: AppColors.violet600,
|
|
632
|
+
borderWidth: 1.5,
|
|
633
|
+
},
|
|
303
634
|
thumbnailContainer: {
|
|
304
635
|
width: '100%',
|
|
305
636
|
height: 110,
|
|
@@ -310,6 +641,50 @@ const galleryStyles = StyleSheet.create({
|
|
|
310
641
|
width: '100%',
|
|
311
642
|
height: '100%',
|
|
312
643
|
},
|
|
644
|
+
thumbnailFallback: {
|
|
645
|
+
width: '100%',
|
|
646
|
+
height: '100%',
|
|
647
|
+
alignItems: 'center',
|
|
648
|
+
justifyContent: 'center',
|
|
649
|
+
backgroundColor: AppColors.slate900,
|
|
650
|
+
},
|
|
651
|
+
checkbox: {
|
|
652
|
+
position: 'absolute',
|
|
653
|
+
top: 6,
|
|
654
|
+
left: 6,
|
|
655
|
+
width: 22,
|
|
656
|
+
height: 22,
|
|
657
|
+
borderRadius: 5,
|
|
658
|
+
borderWidth: 1.5,
|
|
659
|
+
borderColor: 'rgba(255, 255, 255, 0.65)',
|
|
660
|
+
backgroundColor: 'rgba(15, 23, 42, 0.65)',
|
|
661
|
+
alignItems: 'center',
|
|
662
|
+
justifyContent: 'center',
|
|
663
|
+
zIndex: 10,
|
|
664
|
+
},
|
|
665
|
+
checkboxSelected: {
|
|
666
|
+
backgroundColor: AppColors.violet600,
|
|
667
|
+
borderColor: AppColors.violet600,
|
|
668
|
+
},
|
|
669
|
+
miniPlayCircle: {
|
|
670
|
+
position: 'absolute',
|
|
671
|
+
top: '50%',
|
|
672
|
+
left: '50%',
|
|
673
|
+
width: 32,
|
|
674
|
+
height: 32,
|
|
675
|
+
marginTop: -16,
|
|
676
|
+
marginLeft: -16,
|
|
677
|
+
borderRadius: 16,
|
|
678
|
+
backgroundColor: `${AppColors.sky400}D9`,
|
|
679
|
+
alignItems: 'center',
|
|
680
|
+
justifyContent: 'center',
|
|
681
|
+
paddingLeft: 2,
|
|
682
|
+
shadowColor: '#000',
|
|
683
|
+
shadowOffset: {width: 0, height: 2},
|
|
684
|
+
shadowOpacity: 0.4,
|
|
685
|
+
shadowRadius: 4,
|
|
686
|
+
elevation: 4,
|
|
687
|
+
},
|
|
313
688
|
typeBadge: {
|
|
314
689
|
position: 'absolute',
|
|
315
690
|
top: 6,
|
|
@@ -352,4 +727,103 @@ const galleryStyles = StyleSheet.create({
|
|
|
352
727
|
fontSize: 10,
|
|
353
728
|
color: AppColors.whiteAlpha30,
|
|
354
729
|
},
|
|
730
|
+
/* List View Styles */
|
|
731
|
+
listRow: {
|
|
732
|
+
flexDirection: 'row',
|
|
733
|
+
alignItems: 'center',
|
|
734
|
+
backgroundColor: AppColors.slate800,
|
|
735
|
+
borderRadius: 8,
|
|
736
|
+
borderWidth: 1,
|
|
737
|
+
borderColor: AppColors.borderGlassLight,
|
|
738
|
+
padding: 8,
|
|
739
|
+
gap: 10,
|
|
740
|
+
},
|
|
741
|
+
listRowSelected: {
|
|
742
|
+
borderColor: AppColors.violet600,
|
|
743
|
+
borderWidth: 1.5,
|
|
744
|
+
backgroundColor: `${AppColors.violet600}14`,
|
|
745
|
+
},
|
|
746
|
+
listCheckbox: {
|
|
747
|
+
width: 20,
|
|
748
|
+
height: 20,
|
|
749
|
+
borderRadius: 4,
|
|
750
|
+
borderWidth: 1.5,
|
|
751
|
+
borderColor: 'rgba(255, 255, 255, 0.65)',
|
|
752
|
+
backgroundColor: 'rgba(15, 23, 42, 0.65)',
|
|
753
|
+
alignItems: 'center',
|
|
754
|
+
justifyContent: 'center',
|
|
755
|
+
},
|
|
756
|
+
listThumbContainer: {
|
|
757
|
+
width: 46,
|
|
758
|
+
height: 46,
|
|
759
|
+
borderRadius: 6,
|
|
760
|
+
backgroundColor: AppColors.slate850,
|
|
761
|
+
overflow: 'hidden',
|
|
762
|
+
position: 'relative',
|
|
763
|
+
},
|
|
764
|
+
listThumb: {
|
|
765
|
+
width: '100%',
|
|
766
|
+
height: '100%',
|
|
767
|
+
},
|
|
768
|
+
listThumbFallback: {
|
|
769
|
+
width: '100%',
|
|
770
|
+
height: '100%',
|
|
771
|
+
alignItems: 'center',
|
|
772
|
+
justifyContent: 'center',
|
|
773
|
+
backgroundColor: AppColors.slate900,
|
|
774
|
+
},
|
|
775
|
+
listMiniPlay: {
|
|
776
|
+
position: 'absolute',
|
|
777
|
+
top: '50%',
|
|
778
|
+
left: '50%',
|
|
779
|
+
width: 20,
|
|
780
|
+
height: 20,
|
|
781
|
+
marginTop: -10,
|
|
782
|
+
marginLeft: -10,
|
|
783
|
+
borderRadius: 10,
|
|
784
|
+
backgroundColor: `${AppColors.sky400}D9`,
|
|
785
|
+
alignItems: 'center',
|
|
786
|
+
justifyContent: 'center',
|
|
787
|
+
paddingLeft: 1,
|
|
788
|
+
},
|
|
789
|
+
listInfo: {
|
|
790
|
+
flex: 1,
|
|
791
|
+
gap: 4,
|
|
792
|
+
minWidth: 0,
|
|
793
|
+
},
|
|
794
|
+
listTitle: {
|
|
795
|
+
fontFamily: AppFonts.interMedium,
|
|
796
|
+
fontSize: 12,
|
|
797
|
+
color: AppColors.white,
|
|
798
|
+
},
|
|
799
|
+
listMetaRow: {
|
|
800
|
+
flexDirection: 'row',
|
|
801
|
+
alignItems: 'center',
|
|
802
|
+
gap: 6,
|
|
803
|
+
flexWrap: 'wrap',
|
|
804
|
+
},
|
|
805
|
+
listBadge: {
|
|
806
|
+
flexDirection: 'row',
|
|
807
|
+
alignItems: 'center',
|
|
808
|
+
gap: 3,
|
|
809
|
+
backgroundColor: AppColors.badgeGlassDark,
|
|
810
|
+
paddingHorizontal: 4,
|
|
811
|
+
paddingVertical: 1.5,
|
|
812
|
+
borderRadius: 4,
|
|
813
|
+
borderWidth: 1,
|
|
814
|
+
borderColor: AppColors.borderGlassMedium,
|
|
815
|
+
},
|
|
816
|
+
listBadgeText: {
|
|
817
|
+
fontFamily: AppFonts.interBold,
|
|
818
|
+
fontSize: 8.5,
|
|
819
|
+
color: AppColors.slate200,
|
|
820
|
+
},
|
|
821
|
+
listDeleteBtn: {
|
|
822
|
+
padding: 6,
|
|
823
|
+
borderRadius: 6,
|
|
824
|
+
backgroundColor: `${AppColors.red500}14`,
|
|
825
|
+
borderWidth: 1,
|
|
826
|
+
borderColor: `${AppColors.red500}33`,
|
|
827
|
+
},
|
|
355
828
|
});
|
|
829
|
+
|