file details
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
FlatList,
|
||||
Image,
|
||||
Dimensions,
|
||||
ActivityIndicator,
|
||||
ScrollView,
|
||||
} from 'react-native';
|
||||
import { RouteProp, useRoute } from '@react-navigation/native';
|
||||
import { useFile, useFileImage } from '../hooks/useFiles';
|
||||
import { TagChip } from '../components/TagChip';
|
||||
|
||||
const SCREEN_WIDTH = Dimensions.get('window').width;
|
||||
|
||||
type RootStackParamList = {
|
||||
FileDetail: { fileIds: string[]; initialIndex: number };
|
||||
};
|
||||
|
||||
type FileDetailRouteProp = RouteProp<RootStackParamList, 'FileDetail'>;
|
||||
|
||||
function DetailItem({ fileId }: { fileId: string }) {
|
||||
const { data: imageData, isLoading: imageLoading } = useFileImage(fileId);
|
||||
const { data: fileData } = useFile(fileId);
|
||||
const uri = imageData?.data?.url;
|
||||
const file = fileData as any;
|
||||
|
||||
return (
|
||||
<ScrollView style={styles.page} contentContainerStyle={styles.pageContent}>
|
||||
<View style={styles.imageContainer}>
|
||||
{imageLoading ? (
|
||||
<ActivityIndicator size="large" color="#1976D2" />
|
||||
) : uri ? (
|
||||
<Image source={{ uri }} style={styles.image} resizeMode="contain" />
|
||||
) : (
|
||||
<View style={styles.placeholder}>
|
||||
<Text style={styles.placeholderText}>PDF</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View style={styles.details}>
|
||||
<Text style={styles.fileName}>{imageData?.data?.name ?? file?.name ?? fileId}</Text>
|
||||
|
||||
{imageData?.data?.size != null && (
|
||||
<Text style={styles.meta}>
|
||||
Taille : {(imageData.data.size / 1024).toFixed(1)} Ko
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{file?.createdAt && (
|
||||
<Text style={styles.meta}>
|
||||
Ajouté le {new Date(file.createdAt).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric', month: 'long', year: 'numeric',
|
||||
})}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{file?.tags && file.tags.length > 0 && (
|
||||
<View style={styles.tagsSection}>
|
||||
<Text style={styles.sectionLabel}>Tags</Text>
|
||||
<View style={styles.tagsRow}>
|
||||
{file.tags.map((tag: any) => (
|
||||
<TagChip key={tag.id} name={tag.name} />
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{file?.ocrText && (
|
||||
<View style={styles.ocrSection}>
|
||||
<Text style={styles.sectionLabel}>Texte OCR</Text>
|
||||
<Text style={styles.ocrText}>{file.ocrText}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
export function FileDetailScreen() {
|
||||
const route = useRoute<FileDetailRouteProp>();
|
||||
const { fileIds, initialIndex } = route.params;
|
||||
|
||||
const flatListRef = useRef<FlatList>(null);
|
||||
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
data={fileIds}
|
||||
keyExtractor={(item) => item}
|
||||
horizontal
|
||||
pagingEnabled
|
||||
showsHorizontalScrollIndicator={false}
|
||||
initialScrollIndex={initialIndex}
|
||||
getItemLayout={(_, index) => ({
|
||||
length: SCREEN_WIDTH,
|
||||
offset: SCREEN_WIDTH * index,
|
||||
index,
|
||||
})}
|
||||
onMomentumScrollEnd={(e) => {
|
||||
const index = Math.round(e.nativeEvent.contentOffset.x / SCREEN_WIDTH);
|
||||
setCurrentIndex(index);
|
||||
}}
|
||||
renderItem={({ item }) => (
|
||||
<View style={styles.pageWrapper}>
|
||||
<DetailItem fileId={item} />
|
||||
</View>
|
||||
)}
|
||||
/>
|
||||
|
||||
<View style={styles.pagination}>
|
||||
<Text style={styles.paginationText}>
|
||||
{currentIndex + 1} / {fileIds.length}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
pageWrapper: {
|
||||
width: SCREEN_WIDTH,
|
||||
},
|
||||
page: {
|
||||
flex: 1,
|
||||
},
|
||||
pageContent: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
imageContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
image: {
|
||||
width: SCREEN_WIDTH,
|
||||
height: SCREEN_WIDTH,
|
||||
},
|
||||
placeholder: {
|
||||
width: SCREEN_WIDTH,
|
||||
height: SCREEN_WIDTH,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#1a1a2e',
|
||||
},
|
||||
placeholderText: {
|
||||
fontSize: 48,
|
||||
color: '#1976D2',
|
||||
fontWeight: '700',
|
||||
},
|
||||
details: {
|
||||
backgroundColor: '#fff',
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16,
|
||||
marginTop: -16,
|
||||
padding: 20,
|
||||
},
|
||||
fileName: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
color: '#333',
|
||||
marginBottom: 8,
|
||||
},
|
||||
meta: {
|
||||
fontSize: 14,
|
||||
color: '#666',
|
||||
marginBottom: 4,
|
||||
},
|
||||
tagsRow: {
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
gap: 6,
|
||||
marginTop: 8,
|
||||
},
|
||||
tagsSection: {
|
||||
marginTop: 12,
|
||||
},
|
||||
sectionLabel: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#333',
|
||||
marginBottom: 4,
|
||||
},
|
||||
ocrSection: {
|
||||
marginTop: 16,
|
||||
},
|
||||
ocrText: {
|
||||
fontSize: 14,
|
||||
color: '#555',
|
||||
lineHeight: 20,
|
||||
marginTop: 4,
|
||||
},
|
||||
pagination: {
|
||||
position: 'absolute',
|
||||
bottom: 16,
|
||||
alignSelf: 'center',
|
||||
backgroundColor: 'rgba(0,0,0,0.5)',
|
||||
borderRadius: 12,
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 4,
|
||||
},
|
||||
paginationText: {
|
||||
color: '#fff',
|
||||
fontSize: 13,
|
||||
},
|
||||
});
|
||||
+20
-4
@@ -14,6 +14,7 @@ type RootStackParamList = {
|
||||
Home: undefined;
|
||||
Upload: undefined;
|
||||
Scan: undefined;
|
||||
FileDetail: { fileIds: string[]; initialIndex: number };
|
||||
};
|
||||
|
||||
type NavigationProp = NativeStackNavigationProp<RootStackParamList>;
|
||||
@@ -51,13 +52,13 @@ function formatDateLabel(key: string): string {
|
||||
return key.charAt(0).toUpperCase() + key.slice(1);
|
||||
}
|
||||
|
||||
function FileGridItem({ file }: { file: FileItem }) {
|
||||
function FileGridItem({ file, onPress }: { file: FileItem; onPress?: () => void }) {
|
||||
const { data, isLoading } = useFileImage(file.id);
|
||||
|
||||
const uri = data?.data?.url;
|
||||
|
||||
return (
|
||||
<View style={styles.gridItem}>
|
||||
<TouchableOpacity style={styles.gridItem} onPress={onPress} activeOpacity={0.7}>
|
||||
{isLoading ? (
|
||||
<View style={styles.placeholder}>
|
||||
<ActivityIndicator size="small" color="#1976D2" />
|
||||
@@ -70,7 +71,7 @@ function FileGridItem({ file }: { file: FileItem }) {
|
||||
</View>
|
||||
)}
|
||||
<Text style={styles.fileName} numberOfLines={1}>{file.name}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -101,6 +102,12 @@ export function HomeScreen() {
|
||||
const groups = groupByDate(filteredFiles);
|
||||
const groupKeys = Object.keys(groups);
|
||||
|
||||
const fileIdToIndex = useMemo(() => {
|
||||
const map = new Map<string, number>();
|
||||
filteredFiles.forEach((f, i) => map.set(f.id, i));
|
||||
return map;
|
||||
}, [filteredFiles]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<View style={styles.center}>
|
||||
@@ -154,7 +161,16 @@ export function HomeScreen() {
|
||||
<Text style={styles.sectionTitle}>{formatDateLabel(dateKey)}</Text>
|
||||
<View style={styles.grid}>
|
||||
{groupFiles.map((file) => (
|
||||
<FileGridItem key={file.id} file={file} />
|
||||
<FileGridItem
|
||||
key={file.id}
|
||||
file={file}
|
||||
onPress={() =>
|
||||
navigation.navigate('FileDetail', {
|
||||
fileIds: filteredFiles.map((f) => f.id),
|
||||
initialIndex: fileIdToIndex.get(file.id) ?? 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
Reference in New Issue
Block a user