file details

This commit is contained in:
m
2026-07-12 21:12:56 +02:00
parent f142f154f6
commit 7063768e96
3 changed files with 242 additions and 4 deletions
+6
View File
@@ -9,6 +9,7 @@ import { ScanScreen } from './app/scan';
import { SearchScreen } from './app/search';
import { BatchReviewScreen } from './app/batch-review';
import { PendingReviewScreen } from './app/pending-review';
import { FileDetailScreen } from './app/file-detail';
const Stack = createNativeStackNavigator();
const queryClient = new QueryClient();
@@ -29,6 +30,11 @@ export default function App() {
<Stack.Screen name="Search" component={SearchScreen} options={{ title: 'Recherche' }} />
<Stack.Screen name="BatchReview" component={BatchReviewScreen} options={{ title: 'Revue du lot' }} />
<Stack.Screen name="PendingReview" component={PendingReviewScreen} options={{ title: 'Réorganisation' }} />
<Stack.Screen
name="FileDetail"
component={FileDetailScreen}
options={{ title: 'Détails', headerTintColor: '#fff', headerStyle: { backgroundColor: '#000' } }}
/>
</Stack.Navigator>
</NavigationContainer>
<StatusBar style="auto" />
+216
View File
@@ -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
View File
@@ -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>