display date

This commit is contained in:
m
2026-07-12 18:24:58 +02:00
parent da780baf82
commit 778830eb36
7 changed files with 122 additions and 31 deletions
+7 -7
View File
@@ -3,12 +3,12 @@ package config
import "os" import "os"
type Config struct { type Config struct {
Port string Port string
DBPath string DBPath string
OCREndpoint string OCREndpoint string
UploadDir string UploadDir string
HMACSecret string HMACSecret string
ServerHost string ServerHost string
} }
func Load() *Config { func Load() *Config {
@@ -18,7 +18,7 @@ func Load() *Config {
OCREndpoint: envOr("OCR_ENDPOINT", "http://localhost:9090"), OCREndpoint: envOr("OCR_ENDPOINT", "http://localhost:9090"),
UploadDir: envOr("UPLOAD_DIR", "./uploads"), UploadDir: envOr("UPLOAD_DIR", "./uploads"),
HMACSecret: envOr("HMAC_SECRET", "thisismyrandomstring"), HMACSecret: envOr("HMAC_SECRET", "thisismyrandomstring"),
ServerHost: envOr("SERVER_HOST", "http://localhost:8080"), ServerHost: envOr("SERVER_HOST", "http://192.168.1.17:8080"),
} }
} }
+39
View File
@@ -118,6 +118,45 @@ func (q *Queries) ListFiles(ctx context.Context) ([]File, error) {
return items, nil return items, nil
} }
const listFilesByID = `-- name: ListFilesByID :many
SELECT id, name, mime_type, size, storage_key, checksum, ocr_text, created_at, updated_at FROM files
WHERE id IN (SELECT value FROM json_each(?))
ORDER BY created_at DESC
`
func (q *Queries) ListFilesByID(ctx context.Context, jsonEach interface{}) ([]File, error) {
rows, err := q.db.QueryContext(ctx, listFilesByID, jsonEach)
if err != nil {
return nil, err
}
defer rows.Close()
var items []File
for rows.Next() {
var i File
if err := rows.Scan(
&i.ID,
&i.Name,
&i.MimeType,
&i.Size,
&i.StorageKey,
&i.Checksum,
&i.OcrText,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateFile = `-- name: UpdateFile :exec const updateFile = `-- name: UpdateFile :exec
UPDATE files UPDATE files
SET name = ?, mime_type = ?, ocr_text = ?, updated_at = CURRENT_TIMESTAMP SET name = ?, mime_type = ?, ocr_text = ?, updated_at = CURRENT_TIMESTAMP
+5
View File
@@ -6,6 +6,11 @@ WHERE id = ? LIMIT 1;
SELECT * FROM files SELECT * FROM files
ORDER BY created_at DESC; ORDER BY created_at DESC;
-- name: ListFilesByID :many
SELECT * FROM files
WHERE id IN (SELECT value FROM json_each(?))
ORDER BY created_at DESC;
-- name: CreateFile :one -- name: CreateFile :one
INSERT INTO files (id, name, mime_type, size, storage_key, checksum, created_at, updated_at) INSERT INTO files (id, name, mime_type, size, storage_key, checksum, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
+27 -10
View File
@@ -60,21 +60,38 @@ func (h *FileHandler) List(c *gin.Context) {
} }
type fileResponse struct { type fileResponse struct {
ID string `json:"id"` ID string `json:"id"`
URL string `json:"url"` URL string `json:"url"`
Name string `json:"name"` Name string `json:"name"`
Size int64 `json:"size"` Size int64 `json:"size"`
Tags []string `json:"tags"` Tags []string `json:"tags"`
CreatedAt string `json:"createdAt"`
} }
resp := make([]fileResponse, len(files)) resp := make([]fileResponse, len(files))
for i, f := range files { for i, f := range files {
metadata, err := h.files.Get(f.ID)
if err != nil {
resp[i] = fileResponse{
ID: f.ID,
URL: h.urls.GenerateDownloadURL(f.ID),
Name: f.Name,
Size: f.Size,
Tags: []string{},
}
continue
}
resp[i] = fileResponse{ resp[i] = fileResponse{
ID: f.ID, ID: f.ID,
URL: h.urls.GenerateDownloadURL(f.ID), URL: h.urls.GenerateDownloadURL(f.ID),
Name: f.Name, Name: f.Name,
Size: f.Size, Size: f.Size,
Tags: []string{}, Tags: []string{},
CreatedAt: metadata.CreatedAt,
} }
} }
+1 -1
View File
@@ -29,7 +29,7 @@ func (s *URLService) GenerateDownloadURL(fileUUID string) string {
sig := s.sign(fileUUID, expires) sig := s.sign(fileUUID, expires)
return fmt.Sprintf( return fmt.Sprintf(
"%s/api/v1/files/%s?expires=%d&sig=%s", "%s/api/v1/files/download/%s?expires=%d&sig=%s",
s.serverHost, s.serverHost,
fileUUID, fileUUID,
expires, expires,
+35 -13
View File
@@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
import { View, FlatList, StyleSheet, TouchableOpacity, Text, Dimensions, Image } from 'react-native'; import { View, FlatList, StyleSheet, TouchableOpacity, Text, Dimensions, Image, ActivityIndicator } from 'react-native';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useFiles } from '../hooks/useFiles'; import { useFiles, useFileImage } from '../hooks/useFiles';
import { FileItem } from '../types'; import { FileItem } from '../types';
const NUM_COLUMNS = 3; const NUM_COLUMNS = 3;
@@ -20,10 +20,18 @@ type NavigationProp = NativeStackNavigationProp<RootStackParamList>;
type GroupedFiles = Record<string, FileItem[]>; type GroupedFiles = Record<string, FileItem[]>;
function parseBackendDate(dateStr: string): Date | null {
if (!dateStr) return null;
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})\s+(\d{2}):(\d{2}):(\d{2})/);
if (!match) return new Date(dateStr);
return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]), Number(match[4]), Number(match[5]), Number(match[6]));
}
function groupByDate(files: FileItem[]): GroupedFiles { function groupByDate(files: FileItem[]): GroupedFiles {
const groups: GroupedFiles = {}; const groups: GroupedFiles = {};
for (const file of files) { for (const file of files) {
const d = new Date(file.createdAt); const d = parseBackendDate(file.createdAt);
if (!d) continue;
const key = d.toLocaleDateString('fr-FR', { const key = d.toLocaleDateString('fr-FR', {
weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
}); });
@@ -80,16 +88,7 @@ export function HomeScreen() {
<Text style={styles.sectionTitle}>{formatDateLabel(dateKey)}</Text> <Text style={styles.sectionTitle}>{formatDateLabel(dateKey)}</Text>
<View style={styles.grid}> <View style={styles.grid}>
{groupFiles.map((file) => ( {groupFiles.map((file) => (
<View key={file.id} style={styles.gridItem}> <FileGridItem key={file.id} file={file} />
{file.url ? (
<Image source={{ uri: file.url }} style={styles.thumb} />
) : (
<View style={styles.placeholder}>
<Text style={styles.placeholderText}>PDF</Text>
</View>
)}
<Text style={styles.fileName} numberOfLines={1}>{file.name}</Text>
</View>
))} ))}
</View> </View>
</View> </View>
@@ -123,6 +122,29 @@ export function HomeScreen() {
); );
} }
function FileGridItem({ file }: { file: FileItem }) {
const { data, isLoading } = useFileImage(file.id);
const uri = data?.data?.url;
return (
<View style={styles.gridItem}>
{isLoading ? (
<View style={styles.placeholder}>
<ActivityIndicator size="small" color="#1976D2" />
</View>
) : uri ? (
<Image source={{ uri }} style={styles.thumb} />
) : (
<View style={styles.placeholder}>
<Text style={styles.placeholderText}>PDF</Text>
</View>
)}
<Text style={styles.fileName} numberOfLines={1}>{file.name}</Text>
</View>
);
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
+8
View File
@@ -21,6 +21,14 @@ export function useFile(id: string) {
}); });
} }
export function useFileImage(fileId: string) {
return useQuery({
queryKey: ['fileImage', fileId],
queryFn: () => apiClient.get<{ data: { id: string; name: string; url: string; size: number } }>(`${ENDPOINTS.FILE}/${fileId}`),
enabled: !!fileId,
});
}
export function useDeleteFile() { export function useDeleteFile() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();