From e4b6e393d844b2edef4448f0c0514a50eb8fcc91 Mon Sep 17 00:00:00 2001 From: m Date: Wed, 29 Jul 2026 22:37:23 +0200 Subject: [PATCH] fix folders on main page --- backend/internal/db/queries/resources.sql | 4 ++-- backend/internal/db/resources.sql.go | 11 ++++++----- backend/internal/handler/resource.go | 5 +++-- backend/internal/service/resource.go | 13 ++++++++++--- mobile/app/folder.tsx | 4 ++-- mobile/app/index.tsx | 2 +- mobile/hooks/useFiles.ts | 19 +++++++++++++++---- 7 files changed, 39 insertions(+), 19 deletions(-) diff --git a/backend/internal/db/queries/resources.sql b/backend/internal/db/queries/resources.sql index bd1b63c..e5db7fd 100644 --- a/backend/internal/db/queries/resources.sql +++ b/backend/internal/db/queries/resources.sql @@ -23,8 +23,8 @@ VALUES ($1, $2, $3, $4, $5, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING *; -- name: CreateFolder :one -INSERT INTO resources (name, is_folder, owner_id, created_at, updated_at) -VALUES ($1, true, $2, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +INSERT INTO resources (name, is_folder, owner_id, parent_resource_id, created_at, updated_at) +VALUES ($1, true, $2, $3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING *; -- name: ListResourcesByParentID :many diff --git a/backend/internal/db/resources.sql.go b/backend/internal/db/resources.sql.go index 94d5351..550c4d6 100644 --- a/backend/internal/db/resources.sql.go +++ b/backend/internal/db/resources.sql.go @@ -43,18 +43,19 @@ func (q *Queries) CountResourcesByParentAndOwner(ctx context.Context, arg CountR } const createFolder = `-- name: CreateFolder :one -INSERT INTO resources (name, is_folder, owner_id, created_at, updated_at) -VALUES ($1, true, $2, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) +INSERT INTO resources (name, is_folder, owner_id, parent_resource_id, created_at, updated_at) +VALUES ($1, true, $2, $3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP) RETURNING id, name, mime_type, size, checksum, ocr_text, is_folder, parent_resource_id, owner_id, created_at, updated_at ` type CreateFolderParams struct { - Name string `json:"name"` - OwnerID uuid.UUID `json:"owner_id"` + Name string `json:"name"` + OwnerID uuid.UUID `json:"owner_id"` + ParentResourceID uuid.NullUUID `json:"parent_resource_id"` } func (q *Queries) CreateFolder(ctx context.Context, arg CreateFolderParams) (Resource, error) { - row := q.db.QueryRowContext(ctx, createFolder, arg.Name, arg.OwnerID) + row := q.db.QueryRowContext(ctx, createFolder, arg.Name, arg.OwnerID, arg.ParentResourceID) var i Resource err := row.Scan( &i.ID, diff --git a/backend/internal/handler/resource.go b/backend/internal/handler/resource.go index 402a17a..138cb5a 100644 --- a/backend/internal/handler/resource.go +++ b/backend/internal/handler/resource.go @@ -335,14 +335,15 @@ func (h *ResourceHandler) CreateFolder(c *gin.Context) { userID := c.GetString(auth.UserIDKey) var body struct { - Name string `json:"name" binding:"required"` + Name string `json:"name" binding:"required"` + ParentResourceID *string `json:"parent_resource_id"` } if err := c.ShouldBindJSON(&body); err != nil { api.Error(c, http.StatusBadRequest, "INVALID_BODY", "Body must contain 'name'") return } - folder, err := h.resources.CreateFolder(body.Name, userID) + folder, err := h.resources.CreateFolder(body.Name, userID, body.ParentResourceID) if err != nil { api.Error(c, http.StatusInternalServerError, "DB_ERROR", "Failed to create folder") return diff --git a/backend/internal/service/resource.go b/backend/internal/service/resource.go index 4a41d66..5479ff4 100644 --- a/backend/internal/service/resource.go +++ b/backend/internal/service/resource.go @@ -271,7 +271,7 @@ func (s *ResourceService) MoveResources(resourceIDs []string, parentResourceID * }) } -func (s *ResourceService) CreateFolder(name, ownerID string) (*model.Resource, error) { +func (s *ResourceService) CreateFolder(name, ownerID string, parentResourceID *string) (*model.Resource, error) { ownerUUID, _ := uuid.Parse(ownerID) ctx := context.Background() @@ -283,9 +283,16 @@ func (s *ResourceService) CreateFolder(name, ownerID string) (*model.Resource, e qtx := s.queries.WithTx(tx) + parentID := uuid.NullUUID{Valid: false} + if parentResourceID != nil { + pid, _ := uuid.Parse(*parentResourceID) + parentID = uuid.NullUUID{UUID: pid, Valid: true} + } + r, err := qtx.CreateFolder(ctx, db.CreateFolderParams{ - Name: name, - OwnerID: ownerUUID, + Name: name, + OwnerID: ownerUUID, + ParentResourceID: parentID, }) if err != nil { return nil, fmt.Errorf("create folder: %w", err) diff --git a/mobile/app/folder.tsx b/mobile/app/folder.tsx index 0a5a52c..c89188c 100644 --- a/mobile/app/folder.tsx +++ b/mobile/app/folder.tsx @@ -213,13 +213,13 @@ export function FolderScreen() { if (!name) return; const ids = Array.from(selectedIds); try { - const newFolder = await createFolder.mutateAsync(name); + const newFolder = await createFolder.mutateAsync({ name, parentResourceId: folderId }); await moveFiles.mutateAsync({ resourceIds: ids, parentResourceId: newFolder.id }); setTagModalVisible(false); setSelectedIds(new Set()); navigation.navigate('Folder', { folderId: newFolder.id, folderName: newFolder.name }); } catch {} - }, [tagInput, selectedIds, createFolder, moveFiles, navigation]); + }, [tagInput, selectedIds, createFolder, moveFiles, navigation, folderId]); const handleMove = useCallback(async (folderId: string | null) => { const ids = Array.from(selectedIds); diff --git a/mobile/app/index.tsx b/mobile/app/index.tsx index e90104c..e37b2c2 100644 --- a/mobile/app/index.tsx +++ b/mobile/app/index.tsx @@ -318,7 +318,7 @@ export function HomeScreen() { if (!name) return; const ids = Array.from(selectedIds); try { - const newFolder = await createFolder.mutateAsync(name); + const newFolder = await createFolder.mutateAsync({ name }); await moveFiles.mutateAsync({ resourceIds: ids, parentResourceId: newFolder.id }); setTagModalVisible(false); setSelectedIds(new Set()); diff --git a/mobile/hooks/useFiles.ts b/mobile/hooks/useFiles.ts index 59e0e28..1d1e25a 100644 --- a/mobile/hooks/useFiles.ts +++ b/mobile/hooks/useFiles.ts @@ -76,6 +76,8 @@ export function useFiles(parentId?: string | null, page: number = 1, limit: numb const backendRes = await apiClient.get>( `${ENDPOINTS.RESOURCES}?page=${page}&limit=${limit}&thumbnail=thumbnail_small`, ); + const returnedIds = new Set(backendRes.data.map((f) => f.id)); + fileStore.mergeFromBackend( backendRes.data.map((f) => ({ id: f.id, @@ -94,8 +96,11 @@ export function useFiles(parentId?: string | null, page: number = 1, limit: numb ); const cached = fileStore.getRootFiles(); + const validFiles = cached.files.filter( + (f) => !f.backendId || returnedIds.has(f.backendId) || f.source === 'local', + ); return { - data: cached.files.map((r) => recordToUnifiedItem(r)!).filter(Boolean), + data: validFiles.map((r) => recordToUnifiedItem(r)!).filter(Boolean), meta: { page, total: backendRes.meta?.total ?? cached.total }, }; }, @@ -164,7 +169,13 @@ export function useMoveResources() { return useMutation({ mutationFn: ({ resourceIds, parentResourceId }: { resourceIds: string[]; parentResourceId: string | null }) => apiClient.post(ENDPOINTS.MOVE, { resource_ids: resourceIds, parent_resource_id: parentResourceId }), - onSuccess: () => { + onSuccess: (_, { resourceIds, parentResourceId }) => { + for (const id of resourceIds) { + const record = fileStore.getById(id) ?? fileStore.getByBackendId(id); + if (record) { + fileStore.updatePartial(record.id, { parentResourceId: parentResourceId ?? null }); + } + } queryClient.invalidateQueries({ queryKey: ['resources'] }); }, }); @@ -205,8 +216,8 @@ export function useCreateFolder() { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async (name: string) => { - const res = await apiClient.post<{ data: FileItem }>(ENDPOINTS.FOLDERS, { name }); + mutationFn: async ({ name, parentResourceId }: { name: string; parentResourceId?: string }) => { + const res = await apiClient.post<{ data: FileItem }>(ENDPOINTS.FOLDERS, { name, parent_resource_id: parentResourceId }); return res.data; }, onSuccess: () => {