From 5c53d94c0c062246b28c9022a2221b4a047b1aad Mon Sep 17 00:00:00 2001 From: Dionisio Date: Fri, 13 Feb 2026 09:32:16 +0100 Subject: [PATCH] feat: fix favorites display and add star indicator on favorited items - Add ?metadata=true support to GET /api/files/{id} to return JSON metadata instead of binary content (was the root cause of favorites not loading) - Fix favorites loadFileDetails to use metadata endpoint with auth headers - Add star icon on favorited files/folders in grid view (top-left corner) - Add star icon on favorited files/folders in list view (next to name) - Refresh file view when toggling favorites so star appears/disappears - Add CSS styles for .favorite-star and .favorite-star-inline --- src/interfaces/api/handlers/file_handler.rs | 14 +++++++ static/css/favorites.css | 37 ++++++++++++++++++ static/js/favorites.js | 43 ++++++++++----------- static/js/ui.js | 12 ++++++ 4 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/interfaces/api/handlers/file_handler.rs b/src/interfaces/api/handlers/file_handler.rs index 98d5d87c..e545ad7e 100644 --- a/src/interfaces/api/handlers/file_handler.rs +++ b/src/interfaces/api/handlers/file_handler.rs @@ -270,6 +270,20 @@ impl FileHandler { } }; + // ── Metadata-only request ──────────────────────────────────── + if params.get("metadata").map_or(false, |v| v == "true" || v == "1") { + return (StatusCode::OK, Json(serde_json::json!({ + "id": file_dto.id, + "name": file_dto.name, + "path": file_dto.path, + "size": file_dto.size, + "mime_type": file_dto.mime_type, + "folder_id": file_dto.folder_id, + "created_at": file_dto.created_at, + "modified_at": file_dto.modified_at + }))).into_response(); + } + let etag = format!("\"{}-{}\"", id, file_dto.modified_at); // ── ETag (304 Not Modified) ────────────────────────────────── diff --git a/static/css/favorites.css b/static/css/favorites.css index c37c4c81..98f0117c 100644 --- a/static/css/favorites.css +++ b/static/css/favorites.css @@ -92,4 +92,41 @@ .favorite-indicator.active { animation: favorite-pulse 0.3s ease; +} + +/* ── Star badge on file/folder cards (normal view) ── */ + +/* Grid view: star in top-left corner */ +.file-card .favorite-star { + position: absolute; + top: 8px; + left: 8px; + font-size: 14px; + color: #ffc107; + filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.15)); + z-index: 5; + pointer-events: none; + + [dir='rtl'] & { + left: unset; + right: 8px; + } +} + +.file-card .favorite-star.active i { + animation: favorite-pulse 0.3s ease; +} + +/* List view: small star next to the name */ +.favorite-star-inline { + color: #ffc107; + font-size: 11px; + margin-left: 6px; + vertical-align: middle; + filter: drop-shadow(0 0 1px rgba(255, 193, 7, 0.4)); + + [dir='rtl'] & { + margin-left: 0; + margin-right: 6px; + } } \ No newline at end of file diff --git a/static/js/favorites.js b/static/js/favorites.js index edac2eec..87760c64 100644 --- a/static/js/favorites.js +++ b/static/js/favorites.js @@ -234,6 +234,11 @@ const favorites = { `"${name}" added to favorites` ); + // Refresh file view to show star icon + if (window.app.currentSection === 'files' && typeof window.loadFiles === 'function') { + window.loadFiles(); + } + return true; } catch (error) { console.error('Error adding to favorites:', error); @@ -275,6 +280,12 @@ const favorites = { `"${item.name}" removed from favorites` ); } + + // Refresh file view to remove star icon + if (window.app.currentSection === 'files' && typeof window.loadFiles === 'function') { + window.loadFiles(); + } + return true; } @@ -371,7 +382,9 @@ const favorites = { */ async loadFolderDetails(favorite, filesGrid, filesListView) { try { - const response = await fetch(`/api/folders/${favorite.id}`); + const token = localStorage.getItem('oxicloud_token'); + const headers = token ? { 'Authorization': `Bearer ${token}` } : {}; + const response = await fetch(`/api/folders/${favorite.id}`, { headers }); if (response.ok) { const folder = await response.json(); @@ -398,30 +411,14 @@ const favorites = { */ async loadFileDetails(favorite, filesGrid, filesListView) { try { - const response = await fetch(`/api/files/${favorite.id}`); + const token = localStorage.getItem('oxicloud_token'); + const headers = token ? { 'Authorization': `Bearer ${token}` } : {}; + const response = await fetch(`/api/files/${favorite.id}?metadata=true`, { headers }); if (response.ok) { - // Check if the response is JSON or binary data like a PDF - const contentType = response.headers.get('content-type'); - - if (contentType && contentType.includes('application/json')) { - const file = await response.json(); - - // Create UI element with favorite indicator - this.createFavoriteFileElement(file, filesGrid, filesListView); - } else { - // For non-JSON responses (like PDFs or other binary files) - // Create a simplified file element with minimal information - const file = { - id: favorite.id, - name: favorite.name || `File ${favorite.id}`, - mime_type: contentType || 'application/octet-stream', - size: 0, // We don't know the size from this response - modified_at: Math.floor(Date.now() / 1000) // Current time in seconds - }; - - this.createFavoriteFileElement(file, filesGrid, filesListView); - } + const file = await response.json(); + // Create UI element with favorite indicator + this.createFavoriteFileElement(file, filesGrid, filesListView); } else if (response.status === 404) { // File not found, might be deleted console.log(`Favorite file ${favorite.id} not found, removing from favorites`); diff --git a/static/js/ui.js b/static/js/ui.js index 4ccabb1e..2645c3d2 100644 --- a/static/js/ui.js +++ b/static/js/ui.js @@ -666,9 +666,14 @@ const ui = { folderGridElement.dataset.folderId = folder.id; folderGridElement.dataset.folderName = folder.name; folderGridElement.dataset.parentId = folder.parent_id || ""; + + // Check if folder is a favorite + const isFolderFav = window.favorites && window.favorites.isFavorite(folder.id, 'folder'); + folderGridElement.innerHTML = `
+ ${isFolderFav ? '
' : ''}
@@ -813,6 +818,7 @@ const ui = { ${folder.name} + ${isFolderFav ? '' : ''}
${window.i18n ? window.i18n.t('files.file_types.folder') : 'Folder'}
--
@@ -927,9 +933,14 @@ const ui = { // Grid view element const fileGridElement = document.createElement('div'); fileGridElement.className = 'file-card'; + + // Check if file is a favorite + const isFileFav = window.favorites && window.favorites.isFavorite(file.id, 'file'); + fileGridElement.innerHTML = `
+ ${isFileFav ? '
' : ''}
@@ -1037,6 +1048,7 @@ const ui = { ${file.name} + ${isFileFav ? '' : ''}
${typeLabel}
${fileSize}