Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
967a8d0
initial_setup
DecodeX15 Oct 1, 2025
0cdc63b
adding backend + frontend for add and remove from favourite
DecodeX15 Oct 2, 2025
451c29c
fixed backend error
DecodeX15 Oct 2, 2025
ff2c0df
adding some ui
DecodeX15 Oct 2, 2025
fbc80c1
updatinng branch
DecodeX15 Oct 12, 2025
2eec24e
fix redirect issue
DecodeX15 Oct 12, 2025
f56fd66
fix lint eorrr
DecodeX15 Oct 12, 2025
2ca8a6b
lint
DecodeX15 Oct 12, 2025
d18aa76
lint
DecodeX15 Oct 12, 2025
e6e9aaa
add
DecodeX15 Oct 19, 2025
6f1fc8d
fix the refresh page problem
DecodeX15 Oct 19, 2025
68f350c
env issue
DecodeX15 Oct 19, 2025
20a4b41
adding
DecodeX15 Oct 19, 2025
ac4d8bc
lint fix
DecodeX15 Oct 19, 2025
f49e63e
update branch
DecodeX15 Oct 20, 2025
324fff5
adding
DecodeX15 Oct 23, 2025
31bd53d
resolving conflicts
DecodeX15 Oct 23, 2025
8d1bd30
resolved all conflicts
DecodeX15 Oct 23, 2025
964f66c
resolving_conflicts::
DecodeX15 Oct 23, 2025
079b034
branch update
DecodeX15 Oct 24, 2025
34c3cba
update fork
DecodeX15 Oct 28, 2025
7410c13
lintin
DecodeX15 Oct 28, 2025
0aa0c31
update
DecodeX15 Oct 29, 2025
e60bf8c
update
DecodeX15 Oct 29, 2025
e4ceda9
regarding requested changes
DecodeX15 Oct 29, 2025
c70b3f3
add
DecodeX15 Oct 29, 2025
478c2a8
chore: apply black formatting
DecodeX15 Oct 29, 2025
7c102b3
ad
DecodeX15 Oct 29, 2025
4698587
conflists
DecodeX15 Oct 30, 2025
0ea5985
conflicts
DecodeX15 Oct 30, 2025
ef52b6f
add
DecodeX15 Oct 30, 2025
b05919e
update
DecodeX15 Oct 30, 2025
58aa9f6
dad
DecodeX15 Oct 30, 2025
51b736e
add
DecodeX15 Oct 30, 2025
e0e0efa
add
DecodeX15 Oct 30, 2025
ecdca33
add
DecodeX15 Oct 30, 2025
bafd7d6
adding heart button funnction in image card
DecodeX15 Oct 31, 2025
c058d75
fix problem of like in the add the fav page
DecodeX15 Oct 31, 2025
5c448ac
Rename frontend/src/api/scripts/setup_env.sh to frontend/scripts/setu…
rahulharpal1603 Nov 15, 2025
bbb67e2
Rename frontend/src/api/scripts/setup_win.ps1 to frontend/scripts/set…
rahulharpal1603 Nov 15, 2025
09ab8be
Refactor favorite functionality: unify naming conventions, remove unu…
rahulharpal1603 Nov 15, 2025
077199c
Update MediaView to use ROUTES constant for favourites path
rahulharpal1603 Nov 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added __pycache__/app.cpython-313.pyc
Binary file not shown.
29 changes: 29 additions & 0 deletions backend/app/database/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def db_create_images_table() -> None:
thumbnailPath TEXT UNIQUE,
metadata TEXT,
isTagged BOOLEAN DEFAULT 0,
isFavourite BOOLEAN DEFAULT 0,
FOREIGN KEY (folder_id) REFERENCES folders(folder_id) ON DELETE CASCADE
)
"""
Expand Down Expand Up @@ -143,6 +144,7 @@ def db_get_all_images(tagged: Union[bool, None] = None) -> List[dict]:
i.thumbnailPath,
i.metadata,
i.isTagged,
i.isFavourite,
m.name as tag_name
FROM images i
LEFT JOIN image_classes ic ON i.id = ic.image_id
Expand All @@ -169,6 +171,7 @@ def db_get_all_images(tagged: Union[bool, None] = None) -> List[dict]:
thumbnail_path,
metadata,
is_tagged,
is_favourite,
tag_name,
) in results:
if image_id not in images_dict:
Expand All @@ -184,6 +187,7 @@ def db_get_all_images(tagged: Union[bool, None] = None) -> List[dict]:
"thumbnailPath": thumbnail_path,
"metadata": metadata_dict,
"isTagged": bool(is_tagged),
"isFavourite": bool(is_favourite),
"tags": [],
}

Expand Down Expand Up @@ -390,3 +394,28 @@ def db_delete_images_by_ids(image_ids: List[ImageId]) -> bool:
return False
finally:
conn.close()


def db_toggle_image_favourite_status(image_id: str) -> bool:
conn = sqlite3.connect(DATABASE_PATH)
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()
48 changes: 48 additions & 0 deletions backend/app/routes/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
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
from app.logging.setup_logging import get_logger

# Initialize logger
logger = get_logger(__name__)
router = APIRouter()


Expand All @@ -29,6 +33,7 @@ class ImageData(BaseModel):
thumbnailPath: str
metadata: MetadataModel
isTagged: bool
isFavourite: bool
tags: Optional[List[str]] = None


Expand Down Expand Up @@ -60,6 +65,7 @@ def get_all_images(
thumbnailPath=image["thumbnailPath"],
metadata=image_util_parse_metadata(image["metadata"]),
isTagged=image["isTagged"],
isFavourite=image.get("isFavourite", False),
tags=image["tags"],
)
for image in images
Expand All @@ -80,3 +86,45 @@ def get_all_images(
message=f"Unable to retrieve images: {str(e)}",
).model_dump(),
)


# adding add to favourite and remove from favourite routes


class ToggleFavouriteRequest(BaseModel):
image_id: str


@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
)
return {
"success": True,
"image_id": image_id,
"isFavourite": image.get("isFavourite", False),
}

except Exception as e:
logger.error(f"error in /toggle-favourite route: {e}")
raise HTTPException(status_code=500, detail=f"Internal server error: {e}")


class ImageInfoResponse(BaseModel):
id: str
path: str
folder_id: str
thumbnailPath: str
metadata: MetadataModel
isTagged: bool
isFavourite: bool
tags: Optional[List[str]] = None
11 changes: 0 additions & 11 deletions backend/run.bat

This file was deleted.

59 changes: 58 additions & 1 deletion docs/backend/backend_python/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,45 @@
}
}
},
"/images/toggle-favourite": {
"post": {
"tags": [
"Images"
],
"summary": "Toggle Favourite",
"operationId": "toggle_favourite_images_toggle_favourite_post",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ToggleFavouriteRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/face-clusters/{cluster_id}": {
"put": {
"tags": [
Expand Down Expand Up @@ -2094,6 +2133,10 @@
"type": "boolean",
"title": "Istagged"
},
"isFavourite": {
"type": "boolean",
"title": "Isfavourite"
},
"tags": {
"anyOf": [
{
Expand All @@ -2116,7 +2159,8 @@
"folder_id",
"thumbnailPath",
"metadata",
"isTagged"
"isTagged",
"isFavourite"
],
"title": "ImageData"
},
Expand Down Expand Up @@ -2506,6 +2550,19 @@
],
"title": "SyncFolderResponse"
},
"ToggleFavouriteRequest": {
"properties": {
"image_id": {
"type": "string",
"title": "Image Id"
}
},
"type": "object",
"required": [
"image_id"
],
"title": "ToggleFavouriteRequest"
},
"UpdateAITaggingData": {
"properties": {
"updated_count": {
Expand Down
2 changes: 1 addition & 1 deletion frontend/scripts/setup_env.sh
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,4 @@ sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*

echo -e "${GREEN}All required tools and libraries are installed!${NC}"
echo -e "${YELLOW}Note: You may need to restart your terminal or run 'source ~/.bashrc' to use the installed tools.${NC}"
echo -e "${YELLOW}Note: You may need to restart your terminal or run 'source ~/.bashrc' to use the installed tools.${NC}"
2 changes: 1 addition & 1 deletion frontend/scripts/setup_win.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ if (-not (Test-Command cmake)) {
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")

Write-Host "All required tools and libraries are installed!" -ForegroundColor Green
Write-Host "Please restart your computer to ensure all changes take effect." -ForegroundColor Yellow
Write-Host "Please restart your computer to ensure all changes take effect." -ForegroundColor Yellow
11 changes: 11 additions & 0 deletions frontend/src/api/api-functions/togglefav.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { imagesEndpoints } from '../apiEndpoints';
import { apiClient } from '../axiosConfig';
import { APIResponse } from '@/types/API';

export const togglefav = async (image_id: string): Promise<APIResponse> => {
const response = await apiClient.post<APIResponse>(
imagesEndpoints.setFavourite,
{ image_id },
);
return response.data;
};
1 change: 1 addition & 0 deletions frontend/src/api/apiEndpoints.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const imagesEndpoints = {
getAllImages: '/images/',
setFavourite: '/images/toggle-favourite',
};

export const faceClustersEndpoints = {
Expand Down
36 changes: 22 additions & 14 deletions frontend/src/components/Media/ImageCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { AspectRatio } from '@/components/ui/aspect-ratio';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { Check, Heart, Share2 } from 'lucide-react';
import { useState } from 'react';
import { useCallback, useState } from 'react';
import { Image } from '@/types/Media';
import { ImageTags } from './ImageTags';
import { convertFileSrc } from '@tauri-apps/api/core';
import { useToggleFav } from '@/hooks/useToggleFav';

interface ImageCardViewProps {
image: Image;
Expand All @@ -23,12 +24,16 @@ export function ImageCard({
showTags = true,
onClick,
}: ImageCardViewProps) {
const [isFavorite, setIsFavorite] = useState(false);
const [isImageHovered, setIsImageHovered] = useState(false);

// Default to empty array if no tags are provided
const tags = image.tags || [];
const { toggleFavourite } = useToggleFav();

const handleToggleFavourite = useCallback(() => {
if (image?.id) {
toggleFavourite(image.id);
}
}, [image, toggleFavourite]);
return (
<div
className={cn(
Expand Down Expand Up @@ -67,22 +72,25 @@ export function ImageCard({
<Button
variant="ghost"
size="icon"
className="cursor-pointer rounded-full bg-white/20 text-white hover:!bg-white/40 hover:!text-white"
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={(e) => {
console.log(image);
e.stopPropagation();
setIsFavorite(!isFavorite);
handleToggleFavourite();
}}
title={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
aria-label="Add to Favorites"
>
<Heart
className={cn(
'h-5 w-5',
isFavorite ? 'fill-brand-orange text-brand-orange' : '',
)}
/>
<span className="sr-only">Favorite</span>
{image.isFavourite ? (
<Heart className="h-5 w-5" fill="currentColor"></Heart>
) : (
<Heart className="h-5 w-5" />
)}
<span className="sr-only">Favourite</span>
</Button>

<Button
variant="ghost"
size="icon"
Expand Down
9 changes: 0 additions & 9 deletions frontend/src/components/Media/MediaThumbnails.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useRef, useEffect } from 'react';
import { Heart } from 'lucide-react';
import { convertFileSrc } from '@tauri-apps/api/core';

interface MediaThumbnailsProps {
Expand All @@ -11,7 +10,6 @@ interface MediaThumbnailsProps {
currentIndex: number;
showThumbnails: boolean;
onThumbnailClick: (index: number) => void;
favorites: string[];
type?: string;
}

Expand All @@ -20,14 +18,12 @@ export const MediaThumbnails: React.FC<MediaThumbnailsProps> = ({
currentIndex,
showThumbnails,
onThumbnailClick,
favorites,
type = 'image',
}) => {
const scrollContainerRef = useRef<HTMLDivElement>(null);
const thumbnailRefs = useRef<Map<number, HTMLDivElement>>(new Map());
const touchStartRef = useRef(0);
const initialScrollLeftRef = useRef(0);
const isFavorite = (imagePath: string) => favorites.includes(imagePath);

useEffect(() => {
const scrollContainer = scrollContainerRef.current;
Expand Down Expand Up @@ -120,11 +116,6 @@ export const MediaThumbnails: React.FC<MediaThumbnailsProps> = ({
: 'opacity-70 hover:opacity-100'
} cursor-pointer transition-all duration-200 hover:scale-105`}
>
{isFavorite(image.path) && (
<div className="absolute top-1 right-1 z-10 rounded-full bg-black/30 p-0.5">
<Heart className="h-3 w-3 fill-current text-rose-500" />
</div>
)}
<img
src={convertFileSrc(image.thumbnailPath) || '/placeholder.svg'}
alt={`thumbnail-${index}`}
Expand Down
Loading
Loading