Skip to content

Adding functionality for add and remove from favourite#551

Closed
DecodeX15 wants to merge 40 commits into
AOSSIE-Org:mainfrom
DecodeX15:adding_functionality_for_share_and_addto_favourite
Closed

Adding functionality for add and remove from favourite#551
DecodeX15 wants to merge 40 commits into
AOSSIE-Org:mainfrom
DecodeX15:adding_functionality_for_share_and_addto_favourite

Conversation

@DecodeX15
Copy link
Copy Markdown
Member

@DecodeX15 DecodeX15 commented Oct 2, 2025

PR: Fix #538 – Add “Favorites” feature

What this PR does:

Users can now add images to favorites with a single click.
Created a Favorites page to view and manage all favorite images.
All changes are synced with the database.
Added a simple UI for easy management.

Screen.Recording.2025-11-01.050141.1.mp4

Summary by CodeRabbit

  • New Features

    • Mark/unmark images as favourites via a heart control in image cards and media view; UI updates instantly.
    • Favourites page to browse all marked images with its own route and sidebar entry.
    • Client-server toggle action with a frontend hook and API call to persist and refresh favourite status.
  • Documentation

    • API docs updated to expose isFavourite on images and a toggle-favourite endpoint.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 2, 2025

Walkthrough

Adds an isFavourite flag across DB, API, OpenAPI and frontend; implements a backend POST /images/toggle-favourite endpoint and db toggle function; adds frontend API, hook, UI wiring, a Favourites page, sidebar route, and routing constant.

Changes

Cohort / File(s) Summary
Backend Database
backend/app/database/images.py
Add isFavourite column to table DDL and SELECTs; map isFavourite into public image dicts; add db_toggle_image_favourite_status(image_id: str) -> bool to validate, toggle (0↔1), commit/rollback, and log DB errors.
Backend Routes & Models
backend/app/routes/images.py
Add isFavourite: bool to ImageData and ImageInfoResponse; add ToggleFavouriteRequest model and POST /images/toggle-favourite route; import and use logger; return updated favourite state.
Backend Config
backend/run.bat
Removed startup script contents (file cleared).
OpenAPI / Docs
docs/backend/backend_python/openapi.json
Add POST /images/toggle-favourite path and ToggleFavouriteRequest schema; add isFavourite boolean to ImageData (marked required); minor parameter cleanups.
Frontend API Endpoints
frontend/src/api/apiEndpoints.ts
Add imagesEndpoints.setfavourite: '/images/toggle-favourite'.
Frontend API Function
frontend/src/api/api-functions/togglefav.ts
Add togglefav(image_id: string) to POST { image_id } to new endpoint and return APIResponse.
Frontend Hooks
frontend/src/hooks/useToggleFav.ts, frontend/src/hooks/useFavorites.ts
New useToggleFav() hook: mutation calling togglefav, invalidates 'images' tag, exposes toggleFavourite and pending state; tiny whitespace cleanup in useFavorites.ts.
Frontend Components
frontend/src/components/Media/ImageCard.tsx, frontend/src/components/Media/MediaView.tsx
Wire favourite toggle into UI: use image.isFavourite / local isfav state, call useToggleFav().toggleFavourite, adjust icon/styling and handlers, update slideshow/controls and navigation handling.
Frontend Pages & Routing
frontend/src/pages/Home/MyFav.tsx, frontend/src/routes/AppRoutes.tsx, frontend/src/components/Navigation/Sidebar/AppSidebar.tsx, frontend/src/constants/routes.ts
Add MyFav page filtering images by isFavourite; add ROUTES.FAVOURITES; register route; add "Favourites" sidebar item with Heart icon.
Frontend Types
frontend/src/types/Media.ts
Add optional isFavourite?: boolean to Image interface.
Misc Scripts
frontend/scripts/setup_env.sh, frontend/scripts/setup_win.ps1
Non-functional newline/EOF formatting tweaks.

Sequence Diagram(s)

%%{init: {"themeVariables":{"primaryColor":"#E6F0FF","secondaryColor":"#FFF0F6","actorBorder":"#2B6CB0"}}}%%
sequenceDiagram
    participant UI as ImageCard / MediaView
    participant Handler as Local Handler
    participant Hook as useToggleFav
    participant API as apiClient
    participant Backend as Backend API
    participant DB as images DB

    UI->>Handler: user clicks favourite
    Handler->>Hook: toggleFavourite(image_id)
    Hook->>API: POST /images/toggle-favourite { image_id }
    API->>Backend: HTTP request
    Backend->>DB: db_toggle_image_favourite_status toggles 0↔1
    DB-->>Backend: success / error
    Backend-->>API: 200 OK / 4xx
    API-->>Hook: response
    Hook->>Hook: invalidate 'images' cache tag
    Hook-->>Handler: result
    Handler->>UI: update local isfav / re-render
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Extra attention:
    • SQL/DDL correctness for isFavourite and whether a migration is required.
    • Transaction, commit/rollback and logging in db_toggle_image_favourite_status.
    • OpenAPI schema consistency with route request/response.
    • Frontend state sync (local isfav vs global images cache) in ImageCard, MediaView, and MyFav.
    • Confirm intended removal/clearing of backend/run.bat.

Possibly related PRs

Suggested labels

enhancement

Poem

🐇 I nudged a row and toggled a heart,
I hopped through code to play my part.
From query up to UI's light,
Favourites glow in soft delight.
Hooray — a little rabbit's rewrite. ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes to backend/run.bat (content removal) and EOL formatting in script files appear unrelated to favourite functionality and may represent scope creep or accidental modifications. Restore backend/run.bat to its original state and verify the script formatting changes were intentional, or document their purpose in the PR description.
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Adding functionality for add and remove from favourite' directly describes the main objective: implementing favourite toggle functionality for images, which aligns with the changeset's core purpose.
Linked Issues check ✅ Passed All key requirements from issue #538 are implemented: favourite button is functional with backend persistence [backend database column, toggle endpoint], UI updates with filled/empty icons [ImageCard styling], and a separate Favourites page is provided [MyFav component, routing].
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c058d75 and bbb67e2.

📒 Files selected for processing (2)
  • frontend/scripts/setup_env.sh (1 hunks)
  • frontend/scripts/setup_win.ps1 (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/scripts/setup_win.ps1
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
🔇 Additional comments (1)
frontend/scripts/setup_env.sh (1)

118-118: This file appears to be out of scope for the PR objectives.

The only change is a trailing newline at EOF—a non-functional formatting adjustment. However, this file is a setup/environment script and is unrelated to the favorites feature implementation (database schema, API endpoints, UI components).

Given that the past review asked about file relocation and the PR comment history mentions conflict resolution, clarify:

  1. Is this change intentional as part of cleanup or conflict resolution?
  2. Should this file be included in this PR, or should it be reverted?

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (3)
frontend/src/pages/Home/MyFav.tsx (2)

72-86: Consider using the Heart icon component for consistency.

The empty state uses an inline SVG for the heart icon. For consistency with the rest of the UI (which uses Lucide icons), consider using the Heart icon from lucide-react.

Apply this diff:

+import { Heart } from 'lucide-react';

           {/* Heart Icon/Sticker */}
           <div className="mb-6 flex h-32 w-32 items-center justify-center rounded-full bg-muted/50">
-            <svg 
-              className="h-16 w-16 text-muted-foreground/60" 
-              fill="none" 
-              stroke="currentColor" 
-              viewBox="0 0 24 24"
-            >
-              <path 
-                strokeLinecap="round" 
-                strokeLinejoin="round" 
-                strokeWidth={1.5} 
-                d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" 
-              />
-            </svg>
+            <Heart className="h-16 w-16 text-muted-foreground/60" />
           </div>

98-103: Consider using react-router navigation and Button component.

Two minor suggestions:

  1. window.history.back() is not ideal as it relies on browser history. Consider using navigate(ROUTES.HOME) from react-router for more predictable behavior.
  2. The button uses inline classes. Consider using the Button component from your UI library for consistency with the rest of the application.

Apply this diff:

+import { useNavigate } from 'react-router';
+import { Button } from '@/components/ui/button';
+import { ROUTES } from '@/constants/routes';

+  const navigate = useNavigate();

           {/* Optional: Browse Images Button */}
-          <button
-            onClick={() => window.history.back()}
-            className="rounded-lg bg-primary px-6 py-2 cursor-pointer text-primary-foreground transition-colors hover:bg-primary/90"
-          >
+          <Button onClick={() => navigate(`/${ROUTES.HOME}`)}>
             Browse Images
-          </button>
+          </Button>
backend/app/database/images.py (1)

351-382: Consider extracting schema migration and remove commented-out code.

The function mixes runtime schema migration (lines 359-364) with data access logic. While this provides a fallback for existing databases, it's not ideal to perform schema changes in a data access function.

Refactor suggestion:

  1. Remove the runtime schema migration logic since db_create_images_table already includes the isFavourite column (line 45). For existing databases, handle schema updates through a proper migration script.

  2. Remove commented-out print statements (lines 373, 375, 378).

  3. For consistency with other database functions, consider removing or logging the print statements (lines 357, 362) instead of printing directly to stdout.

Example refactored version:

def db_toggle_image_favourite_status(image_id: str) -> bool:
    """Toggle the favourite status of an image."""
    conn = sqlite3.connect(DATABASE_PATH)
    cursor = conn.cursor()
    try:
        # Check if image exists
        cursor.execute("SELECT id FROM images WHERE id = ?", (image_id,))
        if not cursor.fetchone():
            return False
        
        # Toggle favourite status
        cursor.execute(
            """
            UPDATE images
            SET isFavourite = CASE WHEN isFavourite = 1 THEN 0 ELSE 1 END
            WHERE id = ?
            """,
            (image_id,),
        )
        conn.commit()
        return cursor.rowcount > 0
    except Exception as e:
        conn.rollback()
        return False
    finally:
        conn.close()

If you need to support existing databases without the isFavourite column, consider creating a dedicated migration script that runs once during application startup or deployment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3363268 and ff2c0df.

⛔ Files ignored due to path filters (2)
  • __pycache__/app.cpython-313.pyc is excluded by !**/*.pyc
  • utils/__pycache__/cache.cpython-313.pyc is excluded by !**/*.pyc
📒 Files selected for processing (14)
  • backend/app/database/images.py (5 hunks)
  • backend/app/routes/images.py (4 hunks)
  • backend/run.bat (0 hunks)
  • docs/backend/backend_python/openapi.json (5 hunks)
  • frontend/src/api/apiEndpoints.ts (1 hunks)
  • frontend/src/components/Media/ImageCard.tsx (3 hunks)
  • frontend/src/components/Media/MediaView.tsx (6 hunks)
  • frontend/src/components/Navigation/Sidebar/AppSidebar.tsx (2 hunks)
  • frontend/src/constants/routes.ts (1 hunks)
  • frontend/src/hooks/useFavorites.ts (0 hunks)
  • frontend/src/pages/Home/Home.tsx (0 hunks)
  • frontend/src/pages/Home/MyFav.tsx (1 hunks)
  • frontend/src/routes/AppRoutes.tsx (2 hunks)
  • frontend/src/types/Media.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • frontend/src/hooks/useFavorites.ts
  • backend/run.bat
  • frontend/src/pages/Home/Home.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
frontend/src/routes/AppRoutes.tsx (2)
frontend/src/constants/routes.ts (1)
  • ROUTES (1-12)
frontend/src/pages/Home/MyFav.tsx (1)
  • MyFav (14-131)
frontend/src/components/Navigation/Sidebar/AppSidebar.tsx (1)
frontend/src/constants/routes.ts (1)
  • ROUTES (1-12)
frontend/src/pages/Home/MyFav.tsx (10)
frontend/src/features/imageSelectors.ts (2)
  • selectIsImageViewOpen (24-27)
  • selectImages (5-7)
frontend/src/app/store.ts (1)
  • RootState (22-22)
frontend/src/hooks/useQueryExtension.ts (1)
  • usePictoQuery (80-108)
frontend/src/api/api-functions/images.ts (1)
  • fetchAllImages (5-10)
frontend/src/features/loaderSlice.ts (2)
  • showLoader (17-20)
  • hideLoader (21-24)
frontend/src/features/infoDialogSlice.ts (1)
  • showInfoDialog (16-30)
frontend/src/types/Media.ts (1)
  • Image (1-11)
frontend/src/features/imageSlice.ts (1)
  • setImages (22-26)
frontend/src/components/Media/ImageCard.tsx (1)
  • ImageCard (20-120)
frontend/src/components/Media/MediaView.tsx (1)
  • MediaView (27-218)
backend/app/routes/images.py (2)
backend/app/database/images.py (2)
  • db_toggle_image_favourite_status (351-382)
  • db_get_all_images (94-169)
backend/app/routes/face_clusters.py (1)
  • ImageData (40-48)
🔇 Additional comments (18)
frontend/src/routes/AppRoutes.tsx (2)

8-8: LGTM!

The import statement for MyFav follows the existing pattern and is correctly placed.


20-20: LGTM!

The new Favourites route is properly integrated within the Layout and follows the existing routing pattern.

frontend/src/components/Navigation/Sidebar/AppSidebar.tsx (2)

15-15: LGTM!

The Heart icon import is correctly placed and follows the existing pattern for other Lucide icons.


41-41: LGTM!

The Favourites menu item is properly configured and follows the existing pattern for sidebar navigation items.

frontend/src/constants/routes.ts (1)

3-3: LGTM!

The FAVOURITES route constant is correctly added and follows the existing naming convention.

frontend/src/types/Media.ts (1)

8-8: LGTM!

The isFavourite property is correctly added as an optional boolean field, which is appropriate for backward compatibility and aligns with the new favourites feature.

frontend/src/api/apiEndpoints.ts (1)

3-3: LGTM!

The setfavourite endpoint is correctly added and follows the existing pattern for API endpoint definitions.

frontend/src/pages/Home/MyFav.tsx (3)

52-54: Empty handler is appropriate here.

The empty handleCloseMediaView function with explanatory comment is fine. The MediaView component handles closing via Redux actions, so no additional logic is needed here.


24-50: Query and lifecycle management looks good.

The use of usePictoQuery with proper enabled flag, loading states, error handling, and Redux integration is well-implemented and follows best practices.


56-64: Filtering and rendering logic is correct.

The logic to filter favourite images and conditionally render either the empty state or the image grid is well-structured and handles all cases appropriately. The use of favouriteImages for both the grid and MediaView ensures consistent navigation.

Also applies to: 109-130

backend/app/routes/images.py (3)

6-6: LGTM!

The import of db_toggle_image_favourite_status is correctly placed and matches the function definition in backend/app/database/images.py.


11-19: LGTM!

The addition of isFavourite: bool to the ImageData model is consistent with the database schema changes and correctly marks it as a required field.


40-52: LGTM!

The use of image.get("isFavourite", False) provides a safe fallback for cases where the field might not be present, ensuring backward compatibility. The implementation correctly propagates the isFavourite field to the response.

docs/backend/backend_python/openapi.json (3)

899-940: LGTM!

The GET /images/info/{image_id} endpoint is correctly defined with proper path parameter and response schema.


1996-2052: LGTM!

The addition of isFavourite to the ImageData schema is consistent with the backend model changes and correctly marks it as a required boolean field.


2147-2203: LGTM!

The new schemas ImageInfoResponse and ToggleFavouriteRequest are correctly defined and match the corresponding backend models.

Also applies to: 2405-2417

backend/app/database/images.py (2)

31-49: LGTM!

The addition of isFavourite BOOLEAN DEFAULT 0 to the images table schema is correctly implemented and follows the existing pattern for boolean columns.


105-169: LGTM!

The modifications to db_get_all_images correctly retrieve and propagate the isFavourite field, with appropriate boolean conversion.

Comment thread backend/app/routes/images.py
Comment thread backend/app/routes/images.py Outdated
Comment thread docs/backend/backend_python/openapi.json
Comment thread frontend/src/components/Media/ImageCard.tsx Outdated
}: ImageCardProps) {
const dispatch = useDispatch();
const [isFavorite, setIsFavorite] = useState(false);
const [isFavorite, setIsFavorite] = useState(image.isFavourite);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Local state out of sync with props and no backend persistence.

Multiple critical issues:

  1. isFavorite local state is initialized from image.isFavourite but never re-syncs when image.isFavourite changes externally (e.g., when another component updates the same image or on re-fetch).
  2. handle_favourite_toggle only updates local state—it doesn't call the backend API to persist the change.
  3. The UI logic relies on image.isFavourite (lines 80, 86) while the toggle handler updates isFavorite (local state), creating an inconsistency.

This means clicking the favorite button will not persist the change to the backend, and the UI state can become stale or inconsistent with the server.

Solution: Remove the local isFavorite state and rely solely on image.isFavourite from props. Implement handle_favourite_toggle to call the backend API (using the setfavourite endpoint from apiEndpoints.ts) and dispatch a Redux action to update the image in the global store.

Example pattern (adjust to your Redux setup):

-  const [isFavorite, setIsFavorite] = useState(image.isFavourite);
+  // Remove local state; use image.isFavourite from props

-  const handle_favourite_toggle = () => {
-    setIsFavorite(!isFavorite);
-  };
+  const handle_favourite_toggle = async (e: React.MouseEvent) => {
+    e.stopPropagation(); // Prevent card click
+    try {
+      const response = await axios.post(
+        `${API_BASE_URL}${imagesEndpoints.setfavourite}`,
+        { image_id: image.id }
+      );
+      if (response.data.success) {
+        // Dispatch Redux action to update image.isFavourite in store
+        dispatch(updateImageFavourite({ 
+          id: image.id, 
+          isFavourite: response.data.isFavourite 
+        }));
+      }
+    } catch (error) {
+      console.error('Failed to toggle favourite:', error);
+      // Show error notification
+    }
+  };

Additionally, update line 84 to prevent event propagation:

-              onClick={handle_favourite_toggle}
+              onClick={(e) => { e.stopPropagation(); handle_favourite_toggle(e); }}

Also applies to: 34-36

Comment on lines +79 to +95
className={`cursor-pointer rounded-full p-2.5 text-white transition-all duration-300 ${
image.isFavourite
? 'bg-rose-500/80 hover:bg-rose-600 hover:shadow-lg'
: 'bg-white/10 hover:bg-white/20 hover:shadow-lg'
}`}
onClick={handle_favourite_toggle}
>
<Heart
className={cn(
'h-5 w-5',
isFavorite ? 'fill-brand-orange text-brand-orange' : '',
)}
/>
{image.isFavourite ? (
// Filled Heart (when favourite)
<Heart
className="h-5 w-5"
fill="currentColor"
>
</Heart>
) : (
<Heart className="h-5 w-5" />
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Inconsistent state usage in UI logic.

The button styling (lines 79-83) and icon rendering (lines 86-95) read image.isFavourite from props, but the onClick handler (line 84) calls handle_favourite_toggle which toggles the local isFavorite state. This creates a disconnect where the UI renders one state but the handler modifies another.

This is a consequence of the critical issue flagged above. Once you remove the local state and implement proper backend synchronization, this inconsistency will be resolved.

const { viewState, handlers } = useImageViewControls();
const { favorites, toggleFavorite, isFavorite } = useFavorites();

const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Stale local state when navigating images.

isfav is initialized once from currentImage?.isFavourite but doesn't update when currentImage changes (e.g., when the user navigates to the next/previous image). This means the favorite icon state will be incorrect after navigation.

Add a useEffect to sync isfav with currentImage?.isFavourite:

  const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
+
+  useEffect(() => {
+    setIsfav(currentImage?.isFavourite || false);
+  }, [currentImage?.isFavourite]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
useEffect(() => {
setIsfav(currentImage?.isFavourite || false);
}, [currentImage?.isFavourite]);
🤖 Prompt for AI Agents
In frontend/src/components/Media/MediaView.tsx around line 47, the local state
variable isfav is only initialized from currentImage?.isFavourite and does not
update when currentImage changes, causing the favorite icon to become stale when
navigating images; add a useEffect that depends on currentImage (or
currentImage?.isFavourite) and calls setIsfav(currentImage?.isFavourite ||
false) to synchronize the local state whenever the displayed image changes,
ensuring the UI reflects the current image's favorite status.

Comment thread frontend/src/components/Media/MediaView.tsx Outdated
Comment thread frontend/src/components/Media/MediaView.tsx
Comment thread frontend/src/pages/Home/MyFav.tsx Outdated
@DecodeX15
Copy link
Copy Markdown
Member Author

@rahulharpal1603 please review sir

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/backend/backend_python/openapi.json (1)

2146-2283: Resolve extensive merge conflicts in schema definitions.

Unresolved merge conflict markers are breaking the JSON structure and causing numerous parse errors (reported by Biome static analysis).

The conflict spans the ImageInfoResponse and MetadataModel schema definitions. Both schemas should be preserved:

  1. Keep ImageInfoResponse (lines 2147-2272 from HEAD) - needed for the new /images/info/{image_id} endpoint
  2. Keep MetadataModel (lines 2185-2283 from 589d71f) - needed for structured metadata in ImageData

However, align ImageInfoResponse.metadata to reference MetadataModel instead of using type "string":

"ImageInfoResponse": {
  "properties": {
    "id": {"type": "string", "title": "Id"},
    "path": {"type": "string", "title": "Path"},
    "folder_id": {"type": "string", "title": "Folder Id"},
    "thumbnailPath": {"type": "string", "title": "Thumbnailpath"},
    "metadata": {
      "$ref": "#/components/schemas/MetadataModel"
    },
    "isTagged": {"type": "boolean", "title": "Istagged"},
    "isFavourite": {"type": "boolean", "title": "Isfavourite"},
    "tags": {
      "anyOf": [
        {"items": {"type": "string"}, "type": "array"},
        {"type": "null"}
      ],
      "title": "Tags"
    }
  },
  "type": "object",
  "required": ["id", "path", "folder_id", "thumbnailPath", "metadata", "isTagged", "isFavourite"],
  "title": "ImageInfoResponse"
},
"MetadataModel": {
  "properties": {
    "name": {"type": "string", "title": "Name"},
    "date_created": {
      "anyOf": [{"type": "string"}, {"type": "null"}],
      "title": "Date Created"
    },
    "width": {"type": "integer", "title": "Width"},
    "height": {"type": "integer", "title": "Height"},
    "file_location": {"type": "string", "title": "File Location"},
    "file_size": {"type": "integer", "title": "File Size"},
    "item_type": {"type": "string", "title": "Item Type"},
    "latitude": {
      "anyOf": [{"type": "number"}, {"type": "null"}],
      "title": "Latitude"
    },
    "longitude": {
      "anyOf": [{"type": "number"}, {"type": "null"}],
      "title": "Longitude"
    },
    "location": {
      "anyOf": [{"type": "string"}, {"type": "null"}],
      "title": "Location"
    }
  },
  "type": "object",
  "required": ["name", "date_created", "width", "height", "file_location", "file_size", "item_type"],
  "title": "MetadataModel"
}
♻️ Duplicate comments (3)
backend/app/routes/images.py (2)

95-108: Performance issue: fetching all images to find one.

Line 103 calls db_get_all_images() to find a single image, which is highly inefficient and will cause performance degradation as the image collection grows. This issue was previously identified in an earlier review.

Please refer to the previous review comment that proposed implementing db_get_image_by_id() function to address this issue.


120-138: Performance issue: fetching all images to find one.

Lines 123-124 fetch all images from the database to find a single image by ID, which is highly inefficient. This issue was previously identified in an earlier review.

Please refer to the previous review comment that proposed using a dedicated db_get_image_by_id() function to address this issue.

docs/backend/backend_python/openapi.json (1)

860-898: Missing response schema for toggle-favourite endpoint.

Line 882 defines the 200 response schema as an empty object {}, but the backend returns {"success": bool, "image_id": str, "isFavourite": bool}. This issue was previously identified in an earlier review.

Please refer to the previous review comment that proposed adding a ToggleFavouriteResponse schema to properly document the response structure.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ff2c0df and fbc80c1.

📒 Files selected for processing (5)
  • backend/app/database/images.py (5 hunks)
  • backend/app/routes/images.py (4 hunks)
  • docs/backend/backend_python/openapi.json (8 hunks)
  • frontend/src/pages/Home/Home.tsx (0 hunks)
  • frontend/src/types/Media.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • frontend/src/pages/Home/Home.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/app/database/images.py
🧰 Additional context used
🧬 Code graph analysis (1)
backend/app/routes/images.py (2)
backend/app/database/images.py (2)
  • db_toggle_image_favourite_status (384-415)
  • db_get_all_images (119-199)
backend/app/utils/images.py (1)
  • image_util_parse_metadata (493-510)
🪛 Biome (2.1.2)
docs/backend/backend_python/openapi.json

[error] 2146-2146: unexpected character <

(parse)


[error] 2146-2146: unexpected character <

(parse)


[error] 2146-2146: unexpected character <

(parse)


[error] 2146-2146: unexpected character <

(parse)


[error] 2146-2146: unexpected character <

(parse)


[error] 2146-2146: unexpected character <

(parse)


[error] 2146-2146: String values must be double quoted.

(parse)


[error] 2147-2147: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2147-2147: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2183-2184: unexpected character =

(parse)


[error] 2184-2184: unexpected character =

(parse)


[error] 2184-2184: unexpected character =

(parse)


[error] 2184-2184: unexpected character =

(parse)


[error] 2184-2184: unexpected character =

(parse)


[error] 2184-2184: unexpected character =

(parse)


[error] 2184-2184: unexpected character =

(parse)


[error] 2195-2195: unexpected character >

(parse)


[error] 2195-2195: unexpected character >

(parse)


[error] 2195-2195: unexpected character >

(parse)


[error] 2195-2195: unexpected character >

(parse)


[error] 2195-2195: unexpected character >

(parse)


[error] 2195-2195: unexpected character >

(parse)


[error] 2195-2195: expected , but instead found 589

Remove 589

(parse)


[error] 2195-2195: expected , but instead found d71f3ac56247ffd133813c163893bd7762069

Remove d71f3ac56247ffd133813c163893bd7762069

(parse)


[error] 2196-2196: expected , but instead found }

Remove }

(parse)


[error] 2197-2197: Expected a property but instead found '{'.

Expected a property here.

(parse)


[error] 2200-2200: expected , but instead found ]

Remove ]

(parse)


[error] 2200-2201: unexpected character <

(parse)


[error] 2201-2201: unexpected character <

(parse)


[error] 2201-2201: unexpected character <

(parse)


[error] 2201-2201: unexpected character <

(parse)


[error] 2201-2201: unexpected character <

(parse)


[error] 2201-2201: unexpected character <

(parse)


[error] 2201-2201: unexpected character <

(parse)


[error] 2201-2201: String values must be double quoted.

(parse)


[error] 2202-2202: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2202-2202: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2202-2202: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2202-2203: unexpected character =

(parse)


[error] 2203-2203: unexpected character =

(parse)


[error] 2203-2203: unexpected character =

(parse)


[error] 2203-2203: unexpected character =

(parse)


[error] 2203-2203: unexpected character =

(parse)


[error] 2203-2203: unexpected character =

(parse)


[error] 2203-2203: unexpected character =

(parse)


[error] 2258-2258: unexpected character >

(parse)


[error] 2258-2258: unexpected character >

(parse)


[error] 2258-2258: unexpected character >

(parse)


[error] 2258-2258: unexpected character >

(parse)


[error] 2258-2258: unexpected character >

(parse)


[error] 2258-2258: unexpected character >

(parse)


[error] 2258-2258: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2258-2258: String values must be double quoted.

(parse)


[error] 2259-2260: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2261-2261: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2261-2261: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2261-2261: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2261-2261: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2262-2262: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2262-2262: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2262-2263: unexpected character <

(parse)


[error] 2263-2263: unexpected character <

(parse)


[error] 2263-2263: unexpected character <

(parse)


[error] 2263-2263: unexpected character <

(parse)


[error] 2263-2263: unexpected character <

(parse)


[error] 2263-2263: unexpected character <

(parse)


[error] 2263-2263: unexpected character <

(parse)


[error] 2263-2263: expected , but instead found HEAD

Remove HEAD

(parse)


[error] 2264-2264: expected , but instead found "id"

Remove "id"

(parse)


[error] 2262-2271: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2271-2271: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2272-2272: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2272-2272: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2272-2272: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2272-2273: unexpected character =

(parse)


[error] 2273-2273: unexpected character =

(parse)


[error] 2273-2273: unexpected character =

(parse)


[error] 2273-2273: unexpected character =

(parse)


[error] 2273-2273: unexpected character =

(parse)


[error] 2273-2273: unexpected character =

(parse)


[error] 2273-2273: unexpected character =

(parse)


[error] 2282-2283: unexpected character >

(parse)


[error] 2283-2283: unexpected character >

(parse)


[error] 2283-2283: unexpected character >

(parse)


[error] 2283-2283: unexpected character >

(parse)


[error] 2283-2283: unexpected character >

(parse)


[error] 2283-2283: unexpected character >

(parse)


[error] 2283-2283: unexpected character >

(parse)


[error] 2283-2283: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2283-2283: String values must be double quoted.

(parse)


[error] 2486-2486: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2486-2486: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2486-2498: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)


[error] 2498-2498: End of file expected

Use an array for a sequence of values: [1, 2]

(parse)

frontend/src/types/Media.ts

[error] 21-21: Expected an expression, or an assignment but instead found ':'.

Expected an expression, or an assignment here.

(parse)


[error] 21-23: Expected a statement but instead found '=======
metadata?: ImageMetadata'.

Expected a statement here.

(parse)


[error] 24-24: numbers cannot be followed by identifiers directly after

an identifier cannot appear here

(parse)

🪛 GitHub Actions: PR Check Build
frontend/src/types/Media.ts

[error] 19-19: TS1185: Merge conflict marker encountered.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Backend Tests

Comment thread backend/app/routes/images.py Outdated
Comment thread backend/app/routes/images.py Outdated
path: str
folder_id: str
thumbnailPath: str
metadata: str
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Oct 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Type mismatch in ImageInfoResponse.

The metadata field is typed as str, but the ImageData model uses MetadataModel (a structured Pydantic model). This inconsistency means:

  • ImageInfoResponse returns raw string metadata
  • ImageData in / endpoint returns parsed structured metadata
  • Frontend consumers must handle two different metadata formats

Align the type to match ImageData:

 class ImageInfoResponse(BaseModel):
     id: str
     path: str
     folder_id: str
     thumbnailPath: str
-    metadata: str
+    metadata: MetadataModel
     isTagged: bool
     isFavourite: bool
     tags: Optional[List[str]] = None

Then update line 134 to parse the metadata:

     return ImageInfoResponse(
         id=image["id"],
         path=image["path"],
         folder_id=image["folder_id"],
         thumbnailPath=image["thumbnailPath"],
-        metadata=image["metadata"],
+        metadata=image_util_parse_metadata(image["metadata"]),
         isTagged=image["isTagged"],
         isFavourite=image.get("isFavourite", False),
         tags=image.get("tags", [])
     )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
metadata: str
# --- In backend/app/routes/images.py ---
# 1) Update the response model
class ImageInfoResponse(BaseModel):
id: str
path: str
folder_id: str
thumbnailPath: str
metadata: MetadataModel
isTagged: bool
isFavourite: bool
tags: Optional[List[str]] = None
# 2) Parse the raw metadata when constructing the response
@router.get("/info/{image_id}", response_model=ImageInfoResponse)
def get_image_info(image_id: str):
image = db_get_image_by_id(image_id)
if not image:
raise HTTPException(status_code=404, detail="Image not found")
return ImageInfoResponse(
id=image["id"],
path=image["path"],
folder_id=image["folder_id"],
thumbnailPath=image["thumbnailPath"],
metadata=image_util_parse_metadata(image["metadata"]),
isTagged=image["isTagged"],
isFavourite=image.get("isFavourite", False),
tags=image.get("tags", [])
)
🤖 Prompt for AI Agents
In backend/app/routes/images.py around line 115 and again at line 134, change
the ImageInfoResponse.metadata type from str to MetadataModel (import it if not
already), and at line 134 parse the stored metadata string into that model
before returning (e.g., if record.metadata is a JSON string use
MetadataModel.parse_raw(record.metadata), otherwise construct via
MetadataModel(**record.metadata)); ensure the response uses the MetadataModel
instance so both endpoints return the same structured metadata.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Vaibhaviitian
Fix this coderabbit suggestion.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread frontend/src/types/Media.ts Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
frontend/src/pages/Home/MyFav.tsx (1)

58-58: Use English for code comments.

The inline comment is in Hindi. This was previously flagged in past reviews.

Apply this diff:

-  // Sirf favourite wali images ke liye
+  // Filter to show only favourite images
   const favouriteImages = displayImages.filter(
🧹 Nitpick comments (1)
frontend/src/pages/Home/MyFav.tsx (1)

67-108: Nice empty state UX!

The empty state provides clear guidance and a call-to-action button, which is excellent for user experience.

Optional improvement: Consider using a route constant instead of the hardcoded '/' at line 100 for better maintainability:

+import { ROUTES } from '@/constants/routes';
+
 // ...
-            onClick={() => navigate('/')}
+            onClick={() => navigate(ROUTES.HOME)}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2eec24e and f56fd66.

📒 Files selected for processing (4)
  • frontend/src/components/Media/ImageCard.tsx (2 hunks)
  • frontend/src/components/Media/MediaView.tsx (6 hunks)
  • frontend/src/pages/Home/MyFav.tsx (1 hunks)
  • frontend/src/types/Media.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/components/Media/MediaView.tsx
  • frontend/src/components/Media/ImageCard.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/pages/Home/MyFav.tsx (10)
frontend/src/features/imageSelectors.ts (2)
  • selectIsImageViewOpen (24-27)
  • selectImages (5-7)
frontend/src/app/store.ts (1)
  • RootState (22-22)
frontend/src/hooks/useQueryExtension.ts (1)
  • usePictoQuery (80-108)
frontend/src/api/api-functions/images.ts (1)
  • fetchAllImages (5-10)
frontend/src/features/loaderSlice.ts (2)
  • showLoader (17-20)
  • hideLoader (21-24)
frontend/src/features/infoDialogSlice.ts (1)
  • showInfoDialog (16-30)
frontend/src/types/Media.ts (1)
  • Image (13-23)
frontend/src/features/imageSlice.ts (1)
  • setImages (22-26)
frontend/src/components/Media/ImageCard.tsx (1)
  • ImageCard (20-116)
frontend/src/components/Media/MediaView.tsx (1)
  • MediaView (27-218)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Backend Tests
🔇 Additional comments (6)
frontend/src/types/Media.ts (1)

20-20: LGTM! The merge conflict has been resolved correctly.

The isFavourite optional property has been added successfully, retaining the structured metadata field from the other branch. The optional flag enables backward compatibility and aligns with the backend changes and UI components.

frontend/src/pages/Home/MyFav.tsx (5)

1-14: LGTM! Imports are well-organized.

All necessary dependencies are imported and properly used throughout the component.


15-29: LGTM! Component setup and query configuration are correct.

The usePictoQuery hook is properly configured with appropriate query key, function, and enablement logic that avoids redundant fetches when search is active.


31-51: LGTM! Fetch lifecycle is handled correctly.

The effect properly manages loading states, error handling, and success dispatches. The conditional logic ensures it only runs when search is inactive, avoiding conflicts with search results.


57-66: Verify the search integration behavior on the Favourites page.

The current implementation filters search results when isSearchActive is true (line 57), and the title changes to "Face Search Results" on the Favourites page (lines 62-65). This might confuse users who navigate to the Favourites page expecting to see all their favourites, not filtered search results.

Consider whether the Favourites page should:

  1. Always display all favourite images regardless of search state, or
  2. Show filtered search results when a search is active (current behavior)

If option 1 is preferred, the search state should be cleared when navigating to the Favourites page, or the page should ignore search state entirely.


110-131: LGTM! The main render logic is correct.

The grid properly maps over favouriteImages, and MediaView receives the same array, ensuring index consistency for navigation. The modal correctly limits navigation to favourite images only.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
backend/app/database/face_clusters.py (1)

198-200: Unrelated formatting change: parentheses add no value here.

This change wraps the return type in parentheses but doesn't improve readability or align with the PR's favorites feature. The line isn't long enough to require breaking, and this formatting is inconsistent with other functions in the file (e.g., db_get_all_clusters() on Line 108 and db_get_cluster_by_id() on Line 80 use unwrapped return types).

Consider removing this change or applying consistent formatting across all return types in a separate, dedicated formatting PR.

backend/app/database/faces.py (2)

237-239: Unrelated formatting change: parentheses add no value here.

Similar to backend/app/database/face_clusters.py, this change wraps the return type in parentheses without improving readability or contributing to the PR's favorites feature. The formatting is also inconsistent with other functions in this file (e.g., db_get_faces_unassigned_clusters() on Line 212 uses an unwrapped return type).

Consider removing this change or applying consistent formatting across all return types in a separate, dedicated formatting PR.


274-274: Trailing comma is valid but unrelated to PR objectives.

While trailing commas are a valid Python convention that can make diffs cleaner when adding parameters, this change is unrelated to the favorites functionality being implemented in this PR.

Consider deferring such formatting changes to a dedicated formatting PR to keep this PR focused on the feature implementation.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f56fd66 and 2ca8a6b.

📒 Files selected for processing (7)
  • backend/app/database/face_clusters.py (1 hunks)
  • backend/app/database/faces.py (2 hunks)
  • backend/app/database/folders.py (1 hunks)
  • backend/app/database/images.py (5 hunks)
  • backend/app/routes/albums.py (1 hunks)
  • backend/app/routes/images.py (4 hunks)
  • backend/app/utils/images.py (2 hunks)
✅ Files skipped from review due to trivial changes (2)
  • backend/app/routes/albums.py
  • backend/app/database/folders.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/app/database/images.py
  • backend/app/routes/images.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Backend Tests
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
🔇 Additional comments (2)
backend/app/utils/images.py (2)

103-103: AI summary inconsistency.

The AI-generated summary states that return type annotations (-> None and -> Dict[str, int]) were added to these functions, but the annotations were already present (lines 103 and 266 are not marked as changed). Only the trailing commas on the preceding parameter lines (102 and 265) are new.

Also applies to: 266-266


102-102: Trailing commas are good Python style.

Adding trailing commas to function parameters follows Python best practices and improves future diff readability when parameters are extended.

However, note that these formatting changes are unrelated to the PR's stated objective of adding favorites functionality. Consider whether such unrelated stylistic changes should be included in feature PRs or submitted separately.

Also applies to: 265-265

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/app/database/images.py (1)

385-417: Reuse _connect() for consistent connection configuration.

Every other DB helper relies on _connect() so PRAGMA foreign_keys = ON stays enforced. This helper bypasses that, creating a connection without FK enforcement (and duplicating setup logic). Switching to _connect() keeps the behavior consistent.

-def db_toggle_image_favourite_status(image_id: str) -> bool:
-    conn = sqlite3.connect(DATABASE_PATH)
+def db_toggle_image_favourite_status(image_id: str) -> bool:
+    conn = _connect()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca8a6b and d18aa76.

📒 Files selected for processing (1)
  • backend/app/database/images.py (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Backend Tests

Comment thread backend/app/database/images.py
@DecodeX15
Copy link
Copy Markdown
Member Author

@rahulharpal1603 sir please review this

@rahulharpal1603
Copy link
Copy Markdown
Contributor

@Vaibhaviitian
Please resolve the conflicts.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
backend/app/routes/images.py (4)

101-102: Refactor to fetch single image instead of all images.

This endpoint fetches all images from the database just to find one by ID, causing severe performance degradation as the collection grows. This issue was flagged in a previous review.

See the previous review comment for the recommended solution of creating a dedicated db_get_image_by_id() function.


120-120: Type mismatch in ImageInfoResponse metadata field.

The metadata field is typed as str, but ImageData at line 31 uses MetadataModel. This inconsistency was flagged in a previous review and causes clients to handle two different metadata formats.

See the previous review comment for the recommended fix to align with MetadataModel and parse the metadata at line 140.


129-130: Use dedicated single-image query instead of fetching all images.

Lines 129-130 fetch all images from the database to find one by ID, which is highly inefficient. This issue was flagged in a previous review.

See the previous review comment for the recommended solution using a dedicated db_get_image_by_id() function.


140-140: Metadata type mismatch with ImageInfoResponse.

Line 140 returns raw image["metadata"], but according to the relevant code snippet from db_get_all_images(), this is already a dict. However, ImageInfoResponse.metadata at line 120 is typed as str, creating a mismatch. Additionally, this differs from line 61 where metadata is properly parsed using image_util_parse_metadata().

See the previous review comment on line 120 for the complete fix.

🧹 Nitpick comments (2)
backend/app/routes/images.py (2)

7-7: Consider combining imports from the same module.

Lines 3 and 7 both import from app.database.images. For consistency and readability, combine them into a single import statement.

Apply this diff:

-from app.database.images import db_get_all_images
+from app.database.images import db_get_all_images, db_toggle_image_favourite_status
 from app.schemas.images import ErrorResponse
 from app.utils.images import image_util_parse_metadata
 from pydantic import BaseModel
-from app.database.images import db_toggle_image_favourite_status

93-93: Consider adding a response model for consistency.

The endpoint returns a structured response but doesn't declare a response model, unlike /info/{image_id} at line 126. Adding a Pydantic model improves API documentation and type safety.

Define a response model:

class ToggleFavouriteResponse(BaseModel):
    success: bool
    image_id: str
    isFavourite: bool

Then update the decorator:

-@router.post("/toggle-favourite")
+@router.post("/toggle-favourite", response_model=ToggleFavouriteResponse)
 def toggle_favourite(req: ToggleFavouriteRequest):
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d18aa76 and ecdfd5b.

📒 Files selected for processing (1)
  • backend/app/routes/images.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/app/routes/images.py (1)
backend/app/database/images.py (2)
  • db_toggle_image_favourite_status (384-417)
  • db_get_all_images (119-199)
🔇 Additional comments (3)
backend/app/routes/images.py (3)

33-33: LGTM!

The addition of the isFavourite field to ImageData correctly exposes the favourite status in the API response.


63-63: LGTM!

Safely retrieves isFavourite with a sensible default value.


89-90: LGTM!

The ToggleFavouriteRequest model is appropriately defined for the toggle endpoint.

Comment thread backend/app/routes/images.py
Comment thread backend/app/routes/images.py
@DecodeX15 DecodeX15 force-pushed the adding_functionality_for_share_and_addto_favourite branch from 69989b4 to d18aa76 Compare October 19, 2025 06:11
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
frontend/src/components/Media/MediaView.tsx (4)

48-49: Sync isfav with currentImage to prevent stale icon after navigation.

Without syncing, the favourite icon lags when moving between images.

-const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
+const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
+
+// keep local flag in sync with the image being viewed
+useEffect(() => {
+  setIsfav(currentImage?.isFavourite || false);
+}, [currentImage?.isFavourite]);

Also update imports:

-import { useState, useCallback, useMemo } from 'react';
+import { useState, useCallback, useMemo, useEffect } from 'react';

115-120: Remove double toggle; delegate to the unified handler.

Local flip plus server update causes flicker and race risk.

-const handleToggleFavorite = useCallback(() => {
-  if (currentImage) {
-    setIsfav((prev) => !prev);
-    handle_favourite_toggle();
-  }
-}, [currentImage, isfav]);
+const handleToggleFavorite = useCallback(() => {
+  if (currentImage) {
+    handleFavouriteToggle();
+  }
+}, [currentImage, handleFavouriteToggle]);

73-102: Consolidate favourite toggle: optimistic update + rollback, no alerts, no duplicate local toggles.

Current flow mixes alerts, console logs, and a separate local toggle, which can desync UI and feels jarring. Make the handler own the optimistic update, rollback on error, and remove UI‑blocking alerts.

-// handling toogle_favvvvv
-const handle_favourite_toggle = async () => {
-  // console.log('processing ..');
-  if (!currentImage) return;
-  if (import.meta.env.VITE_BACKEND_URL === undefined) {
-    alert('Backend URL is not defined');
-    return;
-  }
-  try {
-    const res = await axios.post(
-      `${import.meta.env.VITE_BACKEND_URL}${imagesEndpoints.setfavourite}`,
-      {
-        image_id: currentImage?.id,
-      },
-    );
-    if (res.data.success) {
-      setIsfav(res.data.isFavourite);
-      await queryclient.invalidateQueries({ queryKey: ['images'] });
-      res?.data?.isFavourite
-        ? alert('Add to Favourite')
-        : alert('Removed from Favourite');
-      console.log('toggled');
-      toggleFavorite(currentImage?.path || '');
-    }
-    console.log(res);
-  } catch (error) {
-    alert('Error toggling favourite');
-    console.log(error);
-  }
-};
+// toggle favourite with optimistic UI and rollback on failure
+const handleFavouriteToggle = async () => {
+  if (!currentImage?.id) return;
+  if (!import.meta.env.VITE_BACKEND_URL) return;
+
+  const previous = isfav;
+  setIsfav(!previous); // optimistic
+  try {
+    const res = await axios.post(
+      `${import.meta.env.VITE_BACKEND_URL}${imagesEndpoints.setfavourite}`,
+      { image_id: currentImage.id },
+    );
+    if (res.data?.success) {
+      setIsfav(Boolean(res.data.isFavourite));
+      await queryClient.invalidateQueries({ queryKey: ['images'] });
+      // TODO: replace with your non-blocking notification (toast/snackbar)
+    } else {
+      setIsfav(previous); // rollback on unexpected payload
+    }
+  } catch (error) {
+    setIsfav(previous); // rollback on error
+    // TODO: replace with your non-blocking error notification
+  }
+};

Optional (recommended): switch to react-query useMutation for pending/disable and cleaner invalidation. Example:

const toggleFav = useMutation({
  mutationFn: (id: number) =>
    axios.post(`${import.meta.env.VITE_BACKEND_URL}${imagesEndpoints.setfavourite}`, { image_id: id })
         .then(r => r.data),
  onMutate: async () => {
    const prev = isfav;
    setIsfav(!prev);
    return { prev };
  },
  onError: (_e, _v, ctx) => setIsfav(ctx?.prev ?? false),
  onSuccess: (data) => setIsfav(Boolean(data.isFavourite)),
  onSettled: () => queryClient.invalidateQueries({ queryKey: ['images'] }),
});

Based on learnings


47-49: Use a single source of truth for favourites; remove useFavorites and derive from images.

The local favourites array (path-based) competes with server isFavourite (id-based). This can drift.

-import { useFavorites } from '@/hooks/useFavorites';
+// remove local favourites; derive from images' isFavourite
-const { favorites, toggleFavorite } = useFavorites();
-const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
+const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
+const favorites = useMemo(
+  () => images.filter((img) => img.isFavourite).map((img) => img.path),
+  [images],
+);
-        toggleFavorite(currentImage?.path || '');
+        // removed: local favourites toggle; server is the source of truth
-          favorites={favorites}
+          favorites={favorites}

And drop the unused import of useFavorites at the top of the file.

Also applies to: 95-96, 211-212

🧹 Nitpick comments (1)
frontend/src/components/Media/MediaView.tsx (1)

139-139: Remove commented debug code.

-// console.log(currentImage);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e6e9aaa and 6f1fc8d.

📒 Files selected for processing (2)
  • frontend/src/components/Media/MediaView.tsx (7 hunks)
  • frontend/src/pages/Home/Home.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/Home/Home.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/components/Media/MediaView.tsx (3)
frontend/src/hooks/useImageViewControls.ts (1)
  • useImageViewControls (11-94)
frontend/src/hooks/useFavorites.ts (1)
  • useFavorites (3-28)
frontend/src/api/apiEndpoints.ts (1)
  • imagesEndpoints (1-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Backend Tests
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Tauri Build Check (windows-latest)

const [showInfo, setShowInfo] = useState(false);
const [showThumbnails, setShowThumbnails] = useState(false);

const queryclient = useQueryClient();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Consistent naming and key verification.

Rename queryclient → queryClient; verify that ['images'] matches actual query keys.

-const queryclient = useQueryClient();
+const queryClient = useQueryClient();
-      await queryclient.invalidateQueries({ queryKey: ['images'] });
+      await queryClient.invalidateQueries({ queryKey: ['images'] });

Run to confirm keys used for image lists/favourites:

Also applies to: 90-90


🏁 Script executed:

#!/bin/bash
# Find react-query usages and query keys related to images/favourites
rg -n --type=ts --type=tsx -C2 "\buse(Query|SuspenseQuery|InfiniteQuery|Mutation)\b|queryKey\s*:\s*\["

# Specifically list all ['images'] keys
rg -n --type=ts --type=tsx -C1 "queryKey\s*:\s*\[\s*'images'\s*\]"

Length of output: 55



---

🏁 Script executed:

```shell
#!/bin/bash
# Search for React Query patterns without explicit tsx type
rg -n --type=typescript -C2 "useQuery|useSuspenseQuery|useInfiniteQuery|useMutation|queryKey"

# Alternative: search using glob pattern
rg -n -g "*.tsx" -g "*.ts" -C1 "queryKey\s*:\s*\[" 

# Find all ['images'] key usages
rg -n -g "*.tsx" -g "*.ts" "'images'"

Length of output: 3175



---

🏁 Script executed:

```shell
#!/bin/bash
# Find all queryclient usages in MediaView.tsx
rg -n -g "*.tsx" -g "*.ts" "queryclient" frontend/src/components/Media/MediaView.tsx

Length of output: 116


Rename queryclient to queryClient and verify query keys.

The variable naming must follow camelCase convention. The query key ['images'] is verified as correct and used consistently across MyFav.tsx, Home.tsx, and AITagging.tsx for image list queries.

Update required at lines 44 and 90 in frontend/src/components/Media/MediaView.tsx:

-const queryclient = useQueryClient();
+const queryClient = useQueryClient();
-await queryclient.invalidateQueries({ queryKey: ['images'] });
+await queryClient.invalidateQueries({ queryKey: ['images'] });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const queryclient = useQueryClient();
const queryClient = useQueryClient();
🤖 Prompt for AI Agents
In frontend/src/components/Media/MediaView.tsx around lines 44 and 90, rename
the variable queryclient to queryClient (camelCase) and update all references to
use queryClient; also verify and ensure the query key ['images'] is used
consistently for image list queries (matching MyFav.tsx, Home.tsx, and
AITagging.tsx) when calling useQuery/useMutation or accessing cached data so no
stale or mismatched keys remain.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
frontend/src/components/Media/MediaView.tsx (4)

48-48: Sync local isfav when currentImage changes.
Local state becomes stale after navigation; add an effect to mirror currentImage?.isFavourite.

// add near other hooks
useEffect(() => {
  setIsfav(currentImage?.isFavourite ?? false);
}, [currentImage?.isFavourite]);

Also ensure the import line includes useEffect.


44-44: Rename queryclient → queryClient.
Follow camelCase and update references (invalidateQueries call).

-  const queryclient = useQueryClient();
+  const queryClient = useQueryClient();
...
-        await queryclient.invalidateQueries({ queryKey: ['images'] });
+        await queryClient.invalidateQueries({ queryKey: ['images'] });

Verify the query key matches list keys used elsewhere.

#!/bin/bash
rg -n --type=ts --type=tsx -C2 "queryKey\s*:\s*\[" | rg -n "images"

Also applies to: 91-91


116-121: Remove redundant local toggle and fix dependencies.

Avoid double-toggling; just call the handler. Update deps.

-  const handleToggleFavorite = useCallback(() => {
-    if (currentImage) {
-      setIsfav((prev) => !prev);
-      handle_favourite_toggle();
-    }
-  }, [currentImage, isfav]);
+  const handleToggleFavorite = useCallback(() => {
+    if (currentImage) {
+      handleFavouriteToggle();
+    }
+  }, [currentImage, handleFavouriteToggle]);

73-104: Consolidate favourite toggle: optimistic update + rollback; remove alerts and duplicate local hook.

Current handler mixes alert UX, redundant local toggleFavorite, dead check for undefined backend URL, and no rollback on error.

Apply this focused rewrite:

-  // handling toogle_favvvvv
-  const handle_favourite_toggle = async () => {
-    // console.log('processing ..');
-    if (!currentImage) return;
-    const backendurl = getBackendUrl();
-    if (backendurl === undefined) {
-      alert('Backend URL is not defined');
-      return;
-    }
-    try {
-      const res = await axios.post(
-        `${backendurl}${imagesEndpoints.setfavourite}`,
-        {
-          image_id: currentImage?.id,
-        },
-      );
-      if (res.data.success) {
-        setIsfav(res.data.isFavourite);
-        await queryclient.invalidateQueries({ queryKey: ['images'] });
-        res?.data?.isFavourite
-          ? alert('Add to Favourite')
-          : alert('Removed from Favourite');
-        console.log('toggled');
-        toggleFavorite(currentImage?.path || '');
-      }
-      console.log(res);
-    } catch (error) {
-      alert('Error toggling favourite');
-      console.log(error);
-    }
-  };
+  // Favourite toggle with optimistic UI and rollback
+  const handleFavouriteToggle = async () => {
+    if (!currentImage?.id) return;
+    const backendUrl = getBackendUrl();
+    const prev = isfav;
+    setIsfav(!prev);
+    try {
+      const res = await axios.post(
+        `${backendUrl}${imagesEndpoints.setfavourite}`,
+        { image_id: currentImage.id },
+      );
+      if (res?.data?.success) {
+        setIsfav(Boolean(res.data.isFavourite));
+        await queryClient.invalidateQueries({ queryKey: ['images'] });
+      } else {
+        setIsfav(prev);
+      }
+    } catch (err) {
+      setIsfav(prev);
+      console.error('Failed to toggle favourite', err);
+    }
+  };

Follow-ups:

  • Drop toggleFavorite/useFavorites from this component to avoid dual sources of truth; rely on server + cache invalidation.
  • Replace alerts with your app’s non-blocking notification (toast/snackbar).
  • Consider a useMutation wrapper for better status handling.
🧹 Nitpick comments (3)
frontend/src/utils/env.ts (1)

1-3: Validate VITE_BACKEND_URL and normalize.

Return type is string but value can be missing at runtime; also normalize trailing slash to avoid double “//”.

Apply this diff:

-export const getBackendUrl = (): string => {
-  return import.meta.env.VITE_BACKEND_URL ;
-};
+export const getBackendUrl = (): string => {
+  const url = (import.meta as any)?.env?.VITE_BACKEND_URL as string | undefined;
+  if (!url) throw new Error('VITE_BACKEND_URL is not defined');
+  return url.replace(/\/$/, '');
+};
frontend/jest.setup.ts (1)

3-5: Remove leftover commented code.

Clean up “REMOVE THESE LINES” block to avoid confusion in setup files.

frontend/src/components/Media/MediaView.tsx (1)

19-19: Type check: backend URL guard is ineffective with current typing.

getBackendUrl returns string; comparing to undefined is dead code. Either throw inside getBackendUrl (preferred) or change to if (!backendurl).

Also applies to: 78-81

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f1fc8d and 68f350c.

📒 Files selected for processing (4)
  • frontend/jest.config.ts (1 hunks)
  • frontend/jest.setup.ts (2 hunks)
  • frontend/src/components/Media/MediaView.tsx (6 hunks)
  • frontend/src/utils/env.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/components/Media/MediaView.tsx (4)
frontend/src/hooks/useImageViewControls.ts (1)
  • useImageViewControls (11-94)
frontend/src/hooks/useFavorites.ts (1)
  • useFavorites (3-28)
frontend/src/utils/env.ts (1)
  • getBackendUrl (1-3)
frontend/src/api/apiEndpoints.ts (1)
  • imagesEndpoints (1-4)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Backend Tests

Comment thread frontend/jest.config.ts Outdated
Comment thread frontend/jest.setup.ts Outdated
Comment on lines +23 to +32
// Direct value do
Object.defineProperty(global, 'import', {
value: {
meta: {
env: {
VITE_BACKEND_URL: 'http://localhost:8000' // Direct value
}
}
}
}); No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Avoid brittle import.meta polyfill; mock the env util instead.

Defining global.import is unreliable for module-scoped import.meta. Prefer mocking '@/utils/env'.

Replace the block with:

jest.mock('@/utils/env', () => ({
  getBackendUrl: () => 'http://localhost:8000',
}));

If you must polyfill, attach to globalThis (still brittle):

Object.defineProperty(globalThis as any, 'import', {
  value: { meta: { env: { VITE_BACKEND_URL: 'http://localhost:8000' } } },
});

Add a search to ensure no other direct import.meta usages leak into tests.


🏁 Script executed:

#!/bin/bash
rg -n --type=ts --type=tsx -C1 "import\.meta"

Length of output: 27


I'll verify the codebase to assess the review comment. The previous script had an error with file type specification. Let me correct it and gather additional information.



🏁 Script executed:

#!/bin/bash
# Search for import.meta usage in TypeScript/JavaScript files
rg -n "import\.meta" --type=ts

# Also search without type restriction to catch all occurrences
echo "---"
echo "Searching without type restriction:"
rg -n "import\.meta"

Length of output: 487


🏁 Script executed:

#!/bin/bash
# Check if @/utils/env file exists
fd "env\.(ts|tsx|js|jsx)$" --path "*utils*"

echo "---"
# Also check the jest.setup.ts file for context
echo "jest.setup.ts content:"
find . -name "jest.setup.ts" -type f -exec cat {} \;

Length of output: 965


🏁 Script executed:

#!/bin/bash
# Search for any mocking of @/utils/env in the codebase
rg "@/utils/env|utils/env" -n

Length of output: 164


Review comment is valid; refactoring suggestion is necessary.

The verification confirms the review comment is correct:

  • @/utils/env exists and exports functions using import.meta.env (specifically getBackendUrl() which is actively imported in MediaView.tsx)
  • The polyfill on global.import is fundamentally brittle: it doesn't affect module-scoped import.meta in ES modules
  • Other utilities like isProd.ts also depend on import.meta.env directly, so the polyfill won't help them
  • The commented-out code in jest.setup.ts (lines 4-5) shows the recommended mocking approach was already considered

The recommendation to mock @/utils/env instead is the correct solution. The current polyfill approach will fail for any code that actually accesses import.meta.env.

@DecodeX15
Copy link
Copy Markdown
Member Author

@Vaibhaviitian Please resolve conflicts.

sir please review it and tell me

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/components/Media/ImageCard.tsx (1)

26-34: Critical: Favorite toggle has no backend persistence.

The handle_favourite_toggle function only updates local component state (isFavorite) without calling the backend API to persist the change. Additionally, the UI logic creates a state inconsistency: the button styling and icon rendering (lines 74-84) read image.isFavourite from props, but the handler updates the disconnected local isFavorite state. This means:

  1. Clicking the favorite button won't persist to the database
  2. The UI won't reflect the change because it reads from props, not local state
  3. State becomes stale when the image prop updates from other sources

Past review comments flagged this exact issue as critical but it remains unresolved.

You should remove the local state entirely and integrate with the backend toggle API. Based on the useToggleFav hook introduced elsewhere in this PR:

-  const [isFavorite, setIsFavorite] = useState(false);
+  const { toggleFavourite } = useToggleFav();

-  const handle_favourite_toggle = () => {
-    setIsFavorite(!isFavorite);
+  const handle_favourite_toggle = (e: React.MouseEvent) => {
+    e.stopPropagation(); // Prevent card click
+    if (image.id) {
+      toggleFavourite(image.id);
+    }
   };

And update the button click handler:

-              onClick={handle_favourite_toggle}
+              onClick={handle_favourite_toggle}

The useToggleFav hook already handles the backend call and query invalidation, so the UI will re-render with updated data from Redux after the mutation succeeds.

♻️ Duplicate comments (9)
backend/app/database/images.py (2)

66-66: Critical: Database migration still missing for existing deployments.

Adding isFavourite only in the CREATE TABLE IF NOT EXISTS statement will cause sqlite3.OperationalError: no such column: i.isFavourite on upgraded instances when db_get_all_images executes. You must add migration logic to detect and add the missing column for existing databases.

Apply this diff to add the migration:

     cursor.execute(
         """
         CREATE TABLE IF NOT EXISTS images (
             id TEXT PRIMARY KEY,
             path VARCHAR UNIQUE,
             folder_id INTEGER,
             thumbnailPath TEXT UNIQUE,
             metadata TEXT,
             isTagged BOOLEAN DEFAULT 0,
             isFavourite BOOLEAN DEFAULT 0,
             FOREIGN KEY (folder_id) REFERENCES folders(folder_id) ON DELETE CASCADE
         )
     """
     )
+
+    # Migration: Add isFavourite column if it doesn't exist
+    cursor.execute("PRAGMA table_info(images)")
+    existing_columns = {column[1] for column in cursor.fetchall()}
+    if "isFavourite" not in existing_columns:
+        cursor.execute(
+            "ALTER TABLE images ADD COLUMN isFavourite BOOLEAN DEFAULT 0"
+        )
+        conn.commit()

399-421: Refactor to use codebase conventions.

The function still has inconsistencies with the rest of the codebase:

  1. Uses sqlite3.connect() directly instead of _connect() helper (line 400)
  2. Missing docstring (all other functions in this module have them)

Apply this diff:

+def db_toggle_image_favourite_status(image_id: str) -> bool:
+    """
+    Toggle the isFavourite status for a specific image.
+
+    Args:
+        image_id: ID of the image to toggle
+
+    Returns:
+        True if toggle was successful, False otherwise
+    """
-def db_toggle_image_favourite_status(image_id: str) -> bool:
-    conn = sqlite3.connect(DATABASE_PATH)
+    conn = _connect()
     cursor = conn.cursor()
     try:
         cursor.execute("SELECT id FROM images WHERE id = ?", (image_id,))
         if not cursor.fetchone():
             return False
         cursor.execute(
             """
             UPDATE images
             SET isFavourite = CASE WHEN isFavourite = 1 THEN 0 ELSE 1 END
             WHERE id = ?
             """,
             (image_id,),
         )
         conn.commit()
         return cursor.rowcount > 0
     except Exception as e:
         logger.error(f"Database error: {e}")
         conn.rollback()
         return False
     finally:
         conn.close()
backend/app/routes/images.py (2)

98-119: Critical: Multiple unfixed issues in toggle_favourite endpoint.

This endpoint still has three unresolved issues from previous reviews:

  1. Performance issue (lines 108-110): Fetching all images to find one is highly inefficient and will degrade as the collection grows.

  2. Potential AttributeError (line 114): If image is None, calling image.get(...) will raise AttributeError.

  3. Exception handling bug (lines 117-119): The broad except Exception will catch the HTTPException raised at line 104, converting a legitimate 404 into a 500 error.

Apply this diff to fix all three issues:

+from app.database.images import db_toggle_image_favourite_status, db_get_image_by_id
+
 @router.post("/toggle-favourite")
 def toggle_favourite(req: ToggleFavouriteRequest):
     image_id = req.image_id
     try:
         success = db_toggle_image_favourite_status(image_id)
         if not success:
             raise HTTPException(
                 status_code=404, detail="Image not found or failed to toggle"
             )
-        # Fetch updated status to return
-        image = next(
-            (img for img in db_get_all_images() if img["id"] == image_id), None
-        )
+        image = db_get_image_by_id(image_id)
+        if not image:
+            raise HTTPException(status_code=404, detail="Image not found after toggle")
+        
         return {
             "success": True,
             "image_id": image_id,
             "isFavourite": image.get("isFavourite", False),
         }
 
+    except HTTPException:
+        raise
     except Exception as e:
         logger.error(f"error in /toggle-favourite route: {e}")
         raise HTTPException(status_code=500, detail=f"Internal server error: {e}")

Note: You'll need to implement db_get_image_by_id in backend/app/database/images.py as suggested in the previous review comment on that file.


122-130: Remove unused ImageInfoResponse model.

This model was added for debugging purposes and has no corresponding endpoint in the codebase. Based on past review comments, it should be removed.

Apply this diff:

-
-
-class ImageInfoResponse(BaseModel):
-    id: str
-    path: str
-    folder_id: str
-    thumbnailPath: str
-    metadata: MetadataModel
-    isTagged: bool
-    isFavourite: bool
-    tags: Optional[List[str]] = None
docs/backend/backend_python/openapi.json (1)

890-928: Define proper response schema for toggle-favourite endpoint.

The 200 response schema is an empty object {} (line 912), but the backend actually returns {"success": bool, "image_id": str, "isFavourite": bool}. This mismatch will cause issues for API consumers and code generators.

Add this schema to the components/schemas section:

"ToggleFavouriteResponse": {
  "properties": {
    "success": {
      "type": "boolean",
      "title": "Success"
    },
    "image_id": {
      "type": "string",
      "title": "Image Id"
    },
    "isFavourite": {
      "type": "boolean",
      "title": "Isfavourite"
    }
  },
  "type": "object",
  "required": [
    "success",
    "image_id",
    "isFavourite"
  ],
  "title": "ToggleFavouriteResponse"
}

Then update the endpoint response reference:

           "200": {
             "description": "Successful Response",
             "content": {
               "application/json": {
-                "schema": {}
+                "schema": {
+                  "$ref": "#/components/schemas/ToggleFavouriteResponse"
+                }
               }
             }
           },
frontend/src/components/Media/MediaView.tsx (3)

149-149: Remove commented debug code.

Commented-out console.log statements should be removed to keep the codebase clean.

The maintainer explicitly requested: "Remove all the unnecessary comments from the files that you have changed."

As per coding guidelines.

  const currentImagePath = currentImage.path;
-  // console.log(currentImage);
  const currentImageAlt = `image-${currentViewIndex}`;

52-52: Critical: Local favorite state not synced with current image.

The isfav state is initialized from currentImage?.isFavourite but never updates when the user navigates to a different image (via next/previous buttons or thumbnails). This causes the favorite icon to display stale state after navigation.

This critical issue was flagged in past reviews but remains unresolved.

Add a useEffect to synchronize isfav with the current image:

  const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
+
+  useEffect(() => {
+    setIsfav(currentImage?.isFavourite || false);
+  }, [currentImage?.isFavourite]);

Alternatively, remove the local state entirely and use currentImage?.isFavourite directly in the UI, updating it via Redux after the mutation succeeds.


110-115: Remove redundant optimistic state update.

The handleToggleFavorite function toggles isfav state optimistically (line 112), then calls handle_favourite_toggle() which invokes the mutation. This creates redundant state updates and the dependency array incorrectly includes isfav.

Past reviews flagged this issue but it remains unresolved.

Remove the redundant setIsfav call and fix the dependency array:

  const handleToggleFavorite = useCallback(() => {
    if (currentImage) {
-      setIsfav((prev) => !prev);
       handle_favourite_toggle();
     }
-  }, [currentImage, isfav]);
+  }, [currentImage, handle_favourite_toggle]);

Note: If you want optimistic updates with proper error handling, implement it inside handle_favourite_toggle with rollback logic on failure. The current implementation has no rollback mechanism.

frontend/src/components/Media/ImageCard.tsx (1)

73-85: Favorite button triggers card click event.

The favorite button's onClick handler doesn't call stopPropagation, so clicking the heart icon will also trigger the card's main onClick handler (line 44), opening the image viewer. This makes the favorite button unusable.

Past review comments from the maintainer noted: "This button is not working. When I try to click the heart icon, the image is opened."

Add event propagation control as shown in the previous comment, or inline:

-              onClick={handle_favourite_toggle}
+              onClick={(e) => {
+                e.stopPropagation();
+                handle_favourite_toggle(e);
+              }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c102b3 and 0ea5985.

📒 Files selected for processing (8)
  • backend/app/database/images.py (5 hunks)
  • backend/app/routes/images.py (4 hunks)
  • docs/backend/backend_python/openapi.json (6 hunks)
  • frontend/src/api/apiEndpoints.ts (1 hunks)
  • frontend/src/components/Media/ImageCard.tsx (2 hunks)
  • frontend/src/components/Media/MediaView.tsx (7 hunks)
  • frontend/src/pages/Home/MyFav.tsx (1 hunks)
  • frontend/src/types/Media.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/api/apiEndpoints.ts
  • frontend/src/types/Media.ts
🧰 Additional context used
🧬 Code graph analysis (3)
frontend/src/components/Media/MediaView.tsx (2)
frontend/src/hooks/useFavorites.ts (1)
  • useFavorites (3-28)
frontend/src/hooks/useToggleFav.ts (1)
  • useToggleFav (5-17)
frontend/src/pages/Home/MyFav.tsx (12)
frontend/src/features/imageSelectors.ts (2)
  • selectIsImageViewOpen (13-16)
  • selectImages (5-7)
frontend/src/components/Media/ChronologicalGallery.tsx (2)
  • MonthMarker (8-12)
  • ChronologicalGallery (23-180)
frontend/src/app/store.ts (1)
  • RootState (22-22)
frontend/src/hooks/useQueryExtension.ts (1)
  • usePictoQuery (80-108)
frontend/src/api/api-functions/images.ts (1)
  • fetchAllImages (5-14)
frontend/src/features/loaderSlice.ts (2)
  • showLoader (17-20)
  • hideLoader (21-24)
frontend/src/features/infoDialogSlice.ts (1)
  • showInfoDialog (16-30)
frontend/src/types/Media.ts (1)
  • Image (13-23)
frontend/src/features/imageSlice.ts (1)
  • setImages (18-20)
frontend/src/components/EmptyStates/EmptyGalleryState.tsx (1)
  • EmptyGalleryState (3-28)
frontend/src/components/Timeline/TimelineScrollbar.tsx (1)
  • TimelineScrollbar (38-409)
frontend/src/components/Media/MediaView.tsx (1)
  • MediaView (27-227)
backend/app/routes/images.py (1)
backend/app/database/images.py (2)
  • db_toggle_image_favourite_status (399-421)
  • db_get_all_images (123-214)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
🔇 Additional comments (8)
backend/app/database/images.py (1)

147-147: LGTM: isFavourite properly integrated into query results.

The additions to select isFavourite, unpack it from results, and include it in the returned dictionaries follow the existing patterns for isTagged.

Also applies to: 174-174, 190-190

backend/app/routes/images.py (2)

7-11: LGTM: Imports and logger properly set up.

The new imports and logger initialization follow the existing codebase patterns.


36-36: LGTM: ImageData model updated correctly.

The isFavourite field is properly added to the model and correctly populated from the database response with a safe default.

Also applies to: 68-68

docs/backend/backend_python/openapi.json (3)

2131-2134: LGTM: ImageData schema properly updated.

The isFavourite field is correctly added as a required boolean, matching the backend model structure.

Also applies to: 2157-2158


2549-2561: LGTM: ToggleFavouriteRequest schema matches backend model.

The request schema correctly defines image_id as a required string field, matching the Pydantic model in the backend.


1120-1122: Minor OpenAPI cleanup looks correct.

The simplification of the InputType parameter reference (removing the allOf wrapper) is a valid cleanup, though unrelated to the favourites feature.

frontend/src/pages/Home/MyFav.tsx (1)

57-59: Verify MediaView close behavior.

The handleCloseMediaView function is empty with a comment stating "MediaView will handle closing via Redux". This is acceptable since MediaView internally dispatches closeImageView() (as seen in MediaView.tsx line 69), but you should verify that the modal closes correctly when:

  1. User clicks the backdrop
  2. User presses Escape key
  3. User clicks the close button

Test the MediaView modal behavior on the Favourites page to confirm it closes properly without an explicit close handler.

frontend/src/components/Media/MediaView.tsx (1)

80-85: Good integration with useToggleFav hook.

The favorite toggle logic correctly uses the useToggleFav hook, which handles the backend API call and cache invalidation. This is a proper implementation following the project's patterns for mutations.

However, ensure you address the state synchronization issues flagged in other comments so the UI properly reflects the favorite status after navigation.

Comment on lines +66 to +69
const title =
isSearchActive && images.length > 0
? `Face Search Results (${images.length} found)`
: 'Favourite Image Gallery';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Questionable: Search state logic on Favourites page.

The title computation checks isSearchActive and shows "Face Search Results" when search is active. This seems out of place on a dedicated Favourites page—users navigating to "Favourites" would not expect to see search results here.

Consider one of these approaches:

  1. Remove search integration entirely (recommended for Favourites page):
  const title =
-    isSearchActive && images.length > 0
-      ? `Face Search Results (${images.length} found)`
-      : 'Favourite Image Gallery';
+    `Favourite Image Gallery (${favouriteImages.length})`;
  1. Or clarify the intent: If search results should affect this page, the filtering logic needs to account for both search results AND favorites (currently it only filters by isFavourite).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const title =
isSearchActive && images.length > 0
? `Face Search Results (${images.length} found)`
: 'Favourite Image Gallery';
const title =
`Favourite Image Gallery (${favouriteImages.length})`;
🤖 Prompt for AI Agents
In frontend/src/pages/Home/MyFav.tsx around lines 66 to 69, the title uses
isSearchActive to switch to "Face Search Results" which is inappropriate for the
dedicated Favourites page; either remove the search integration and always set
the title to "Favourite Image Gallery", or if search must apply, update the
filtering to combine the search results with isFavourite (filter images by both
the search predicate and isFavourite) and change the title to reflect scoped
results (e.g. "Face Search Results (N found in favourites)") so the UI and data
filtering stay consistent.

Comment on lines +101 to +108
{favouriteImages.length > 0 ? (
<ChronologicalGallery
images={favouriteImages}
showTitle={true}
title={title}
onMonthOffsetsChange={setMonthMarkers}
scrollContainerRef={scrollableRef}
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify index mapping between filtered array and MediaView.

ChronologicalGallery receives the filtered favouriteImages array and will render ImageCard components. When a card is clicked, based on the ChronologicalGallery implementation, it dispatches setCurrentViewIndex with a chronological index computed from the full image set via imageIndexMap.

However, you should verify that:

  1. The imageIndexMap in ChronologicalGallery correctly maps filtered favourite images back to their global indices
  2. MediaView displays the correct image when opened from this page

🏁 Script executed:

#!/bin/bash
# Verify ChronologicalGallery's imageIndexMap implementation
ast-grep --pattern $'const imageIndexMap = $$$
$$$
imageIndexMap[$_] = $_
$$$'

# Check how ImageCard dispatches index on click
ast-grep --pattern $'onClick={() => {
  $$$
  dispatch(setCurrentViewIndex($_))
  $$$
}}'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find ChronologicalGallery component file
fd -e tsx -e ts | xargs rg -l "ChronologicalGallery" | head -10

# Search for imageIndexMap usage
rg -n "imageIndexMap" --type=tsx --type=ts -C 3

# Search for setCurrentViewIndex dispatch patterns
rg -nP "setCurrentViewIndex|dispatch.*CurrentViewIndex" --type=tsx --type=ts -C 2

Length of output: 740


🏁 Script executed:

#!/bin/bash
# Locate ImageCard component and its onClick handler
fd -e tsx -e ts | xargs rg -l "ImageCard" | head -10

# Search for onClick handlers that dispatch index changes
rg -nP "onClick.*=>\s*\{" --type=tsx --type=ts -A 5 | head -50

Length of output: 604


🏁 Script executed:

#!/bin/bash
# Check MyFav.tsx more thoroughly to see how ChronologicalGallery is used
rg -n "ChronologicalGallery\|favouriteImages\|scrollableRef" frontend/src/pages/Home/MyFav.tsx -B 2 -A 5

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Examine ChronologicalGallery component for imageIndexMap
cat frontend/src/components/Media/ChronologicalGallery.tsx

Length of output: 5838


🏁 Script executed:

#!/bin/bash
# Examine ImageCard component for onClick handler
cat frontend/src/components/Media/ImageCard.tsx

Length of output: 3667


🏁 Script executed:

#!/bin/bash
# Check MyFav.tsx for complete context
cat frontend/src/pages/Home/MyFav.tsx

Length of output: 4244


🏁 Script executed:

#!/bin/bash
# Examine MediaView to see how it uses currentViewIndex
cat frontend/src/components/Media/MediaView.tsx

Length of output: 6804


🏁 Script executed:

#!/bin/bash
# Search for setCurrentViewIndex usage to understand what index it expects
rg -n "setCurrentViewIndex\|currentViewIndex" frontend/src/features/imageSlice.ts -B 2 -A 5

Length of output: 0


Fix index mapping to use global image IDs instead of filtered gallery positions.

When ImageCard is clicked in the filtered favourite gallery, imageIndexMap provides an index relative to the filtered favouriteImages array (lines 196-198 of ChronologicalGallery.tsx). However, MediaView uses currentViewIndex directly on the global images array (line 40 of MediaView.tsx), causing the wrong image to display.

For example: if global images are [img1, img2, img3, img4, img5] and filtered favourites are [img2, img4], clicking img2 would dispatch index 0 but MediaView would show images[0] = img1.

Resolution: Pass the global image ID (or global index) to setCurrentViewIndex, then resolve it in MediaView using the image's ID or by finding its position in the global array. Alternatively, modify imageIndexMap to map to global indices instead of filtered positions.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (8)
frontend/src/pages/Home/MyFav.tsx (2)

55-57: Remove commented-out code.

The commented handleCloseMediaView function is unused since MediaView is not rendered in this component. Remove this dead code as requested by the maintainer.

Apply this diff:

-  // const handleCloseMediaView = () => {
-  //   // MediaView will handle closing via Redux
-  // };
-

64-67: Remove search state logic from Favourites page.

The title computation showing "Face Search Results" is inappropriate for a dedicated Favourites page. Users navigating to "Favourites" expect to see their favourite images, not search results.

Apply this diff:

-  const title =
-    isSearchActive && images.length > 0
-      ? `Face Search Results (${images.length} found)`
-      : 'Favourite Image Gallery';
+  const title = `Favourite Image Gallery (${favouriteImages.length})`;
frontend/src/components/Media/MediaView.tsx (6)

3-3: Remove commented-out import.

The commented import should be removed as requested by the maintainer to clean up unnecessary comments.

Apply this diff:

-// import { revealItemInDir } from '@tauri-apps/plugin-opener';

34-36: Remove unnecessary comments.

As requested by the maintainer, remove these debug/guard explanation comments.

Apply this diff:

-  // guard: images default to empty array in the signature so `images.length` is safe
-  // keep debug output minimal
-  // console.log(totalImages);

100-100: Remove commented-out code.

The commented revealItemInDir call should be removed per maintainer's request.

Apply this diff:

-      // await revealItemInDir(currentImage.path);

152-152: Remove commented console.log.

Remove this debug statement as requested by the maintainer.

Apply this diff:

-  // console.log(currentImage);

55-55: Sync isfav state when currentImage changes.

The isfav state is initialized once but doesn't update when the user navigates to a different image, causing the favorite icon to show stale state.

Apply this diff:

+import { useEffect } from 'react';
+
 const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
+
+useEffect(() => {
+  setIsfav(currentImage?.isFavourite || false);
+}, [currentImage?.isFavourite]);

113-118: Remove redundant local state toggle.

The handleToggleFavorite function redundantly toggles isfav before calling handle_favourite_toggle. Since the favourite mutation already handles state updates and cache invalidation, this creates unnecessary state churn. The proper state sync should come from the useEffect that watches currentImage?.isFavourite (once added per previous comment).

Apply this diff:

  const handleToggleFavorite = useCallback(() => {
    if (currentImage) {
-      setIsfav((prev) => !prev);
       handle_favourite_toggle();
    }
-  }, [currentImage, isfav]);
+  }, [currentImage, handle_favourite_toggle]);
🧹 Nitpick comments (1)
frontend/src/pages/Home/MyFav.tsx (1)

11-11: Minor: Remove trailing space in import.

There's an extra space after selectImages} in the import statement.

Apply this diff:

-import { selectImages} from '@/features/imageSelectors';
+import { selectImages } from '@/features/imageSelectors';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ea5985 and e0e0efa.

📒 Files selected for processing (3)
  • frontend/src/components/Media/MediaView.tsx (7 hunks)
  • frontend/src/pages/Home/MyFav.tsx (1 hunks)
  • frontend/src/types/Media.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/types/Media.ts
🧰 Additional context used
🧬 Code graph analysis (2)
frontend/src/pages/Home/MyFav.tsx (11)
frontend/src/features/imageSelectors.ts (1)
  • selectImages (5-7)
frontend/src/components/Media/ChronologicalGallery.tsx (2)
  • MonthMarker (10-14)
  • ChronologicalGallery (25-187)
frontend/src/app/store.ts (1)
  • RootState (22-22)
frontend/src/hooks/useQueryExtension.ts (1)
  • usePictoQuery (80-108)
frontend/src/api/api-functions/images.ts (1)
  • fetchAllImages (5-14)
frontend/src/features/loaderSlice.ts (2)
  • showLoader (17-20)
  • hideLoader (21-24)
frontend/src/features/infoDialogSlice.ts (1)
  • showInfoDialog (16-30)
frontend/src/types/Media.ts (1)
  • Image (13-23)
frontend/src/features/imageSlice.ts (1)
  • setImages (18-20)
frontend/src/components/EmptyStates/EmptyGalleryState.tsx (1)
  • EmptyGalleryState (3-28)
frontend/src/components/Timeline/TimelineScrollbar.tsx (1)
  • TimelineScrollbar (38-409)
frontend/src/components/Media/MediaView.tsx (5)
frontend/src/types/Media.ts (1)
  • MediaViewProps (35-39)
frontend/src/features/imageSelectors.ts (1)
  • selectCurrentViewIndex (9-10)
frontend/src/hooks/useFavorites.ts (1)
  • useFavorites (3-28)
frontend/src/hooks/useToggleFav.ts (1)
  • useToggleFav (5-17)
frontend/src/hooks/useSlideshow.ts (1)
  • useSlideshow (3-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Backend Tests
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
🔇 Additional comments (2)
frontend/src/pages/Home/MyFav.tsx (2)

69-90: LGTM: Well-implemented empty state.

The empty state provides clear messaging and uses the Heart icon from lucide-react as requested by the maintainer. The UX guidance ("Click the heart icon on any image") is helpful.


99-109: Remove redundant conditional check.

The check favouriteImages.length > 0 at line 99 is redundant because the component already returns early at line 69 if favouriteImages.length === 0. This means the else branch (lines 107-109) rendering EmptyGalleryState is unreachable.

Apply this diff:

-        {favouriteImages.length > 0 ? (
-          <ChronologicalGallery
-            images={favouriteImages}
-            showTitle={true}
-            title={title}
-            onMonthOffsetsChange={setMonthMarkers}
-            scrollContainerRef={scrollableRef}
-          />
-        ) : (
-          <EmptyGalleryState />
-        )}
+        <ChronologicalGallery
+          images={favouriteImages}
+          showTitle={true}
+          title={title}
+          onMonthOffsetsChange={setMonthMarkers}
+          scrollContainerRef={scrollableRef}
+        />

Likely an incorrect or invalid review comment.

@DecodeX15
Copy link
Copy Markdown
Member Author

@rahulharpal1603 please review sir

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
frontend/src/components/Media/ImageCard.tsx (1)

28-28: Remove redundant local state.

The isfav state is never synchronized with image.isFavourite (initialized to false regardless of actual value), is not used anywhere in the UI (lines 83, 93 read from image.isFavourite instead), and serves no purpose. The useToggleFav hook already auto-invalidates the 'images' cache, so image.isFavourite will update automatically after the mutation completes.

Apply this diff to remove the redundant state:

-  const [isfav, setIsfav] = useState(false);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e0e0efa and bafd7d6.

📒 Files selected for processing (2)
  • frontend/src/components/Media/ImageCard.tsx (3 hunks)
  • frontend/src/pages/Home/MyFav.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/Home/MyFav.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
frontend/src/components/Media/ImageCard.tsx (1)
frontend/src/hooks/useToggleFav.ts (1)
  • useToggleFav (5-17)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Tauri Build Check (windows-latest)
  • GitHub Check: Tauri Build Check (macos-latest, --target aarch64-apple-darwin)
  • GitHub Check: Tauri Build Check (ubuntu-22.04)
  • GitHub Check: Backend Tests
🔇 Additional comments (1)
frontend/src/components/Media/ImageCard.tsx (1)

82-97: UI logic correctly reflects favorite state from props.

The button styling and icon rendering properly use image.isFavourite from props. Once the redundant local state is removed and the mutation hook completes, the cache invalidation will automatically update image.isFavourite, providing a clean unidirectional data flow.

Comment on lines +38 to +43
const handleToggleFavorite = useCallback(() => {
if (image) {
setIsfav((prev) => !prev);
handle_favourite_toggle();
}
}, [image, isfav]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove unnecessary wrapper and fix useCallback dependencies.

Two issues:

  1. Incorrect useCallback dependency: Including isfav in the dependency array causes the callback to be recreated on every toggle, defeating the purpose of memoization.
  2. Unnecessary wrapper: This function simply toggles redundant local state and calls handle_favourite_toggle. Once the local state is removed (per previous comment), this wrapper adds no value.

Apply this diff after removing the isfav state:

-  const handleToggleFavorite = useCallback(() => {
-    if (image) {
-      setIsfav((prev) => !prev);
-      handle_favourite_toggle();
-    }
-  }, [image, isfav]);

Then update line 90 to call handle_favourite_toggle directly:

-                handleToggleFavorite();
+                handle_favourite_toggle();

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In frontend/src/components/Media/ImageCard.tsx around lines 38 to 43, the
handleToggleFavorite wrapper is unnecessary and its useCallback dependency
incorrectly includes isfav; after removing the local isfav state, delete this
handleToggleFavorite function (and its useCallback) and update the call site at
line 90 to invoke handle_favourite_toggle() directly (no wrapper, no extra
deps).

: 'bg-white/10 hover:bg-white/20 hover:shadow-lg'
}`}
onClick={(e) => {
console.log(image);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove debugging artifact.

The console.log(image) statement should be removed before merging.

Apply this diff:

              onClick={(e) => {
-                console.log(image);
                e.stopPropagation();
                handleToggleFavorite();
              }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log(image);
onClick={(e) => {
e.stopPropagation();
handleToggleFavorite();
}}
🤖 Prompt for AI Agents
In frontend/src/components/Media/ImageCard.tsx around line 88, remove the
debugging artifact by deleting the console.log(image) statement; ensure no other
code depends on that log and that formatting/linting remain clean after removal.

Copy link
Copy Markdown
Contributor

@rahulharpal1603 rahulharpal1603 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Vaibhaviitian

I have made some changes. Look at this commit: 09ab8be

This will help you in future to write an even better code.

@DecodeX15
Copy link
Copy Markdown
Member Author

@rahulharpal1603 thanks sir, definitely i will read this

@DecodeX15
Copy link
Copy Markdown
Member Author

@rahulharpal1603 wait sir i am resolving conflicts

@rahulharpal1603
Copy link
Copy Markdown
Contributor

@Vaibhaviitian

No need to resolve the conflicts. I have merged the PR with your changes as well as mine. See this PR: #634

In future also, when you add your contributions to the gsoc proposal use the #634 PR link. I am closing this PR because the above PR also has your commits.

@DecodeX15
Copy link
Copy Markdown
Member Author

DecodeX15 commented Nov 15, 2025

@rahulharpal1603 sir just one doubt as that one is not made by me so someone might think that this one is not mine contribution even though my commits are there

@rahulharpal1603
Copy link
Copy Markdown
Contributor

@rahulharpal1603 sir just one doubt as that one is not made by me so someone might think that this one is not mine contribution even though my commits are there

No it's not like that. I had to squash and merge the PR because there were 40 commits and I wanted to keep the commit history clean.

I always know that it was your PR :) And I am assigning you that PR so that anyone who opens it up will know that you did the major work.

@rahulharpal1603
Copy link
Copy Markdown
Contributor

@rahulharpal1603 sir just one doubt as that one is not made by me so someone might think that this one is not mine contribution even though my commits are there

Comment below that PR so I can assign it to you.

@DecodeX15
Copy link
Copy Markdown
Member Author

DecodeX15 commented Nov 15, 2025

Sir which pr #634 or i have to create new one

@rahulharpal1603
Copy link
Copy Markdown
Contributor

Sir which pr #634 or i have to create new one

comment on 634 only

@DecodeX15
Copy link
Copy Markdown
Member Author

okay sir

@DecodeX15
Copy link
Copy Markdown
Member Author

DecodeX15 commented Mar 29, 2026

This pr was
merged as #634

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat:Add functionality for Favourite button

2 participants