Skip to content

Commit 09ab8be

Browse files
Refactor favorite functionality: unify naming conventions, remove unused favorites hook, and enhance input schema in OpenAPI spec
1 parent bbb67e2 commit 09ab8be

11 files changed

Lines changed: 66 additions & 130 deletions

File tree

docs/backend/backend_python/openapi.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,9 +1117,14 @@
11171117
"in": "query",
11181118
"required": false,
11191119
"schema": {
1120-
"$ref": "#/components/schemas/InputType",
1120+
"allOf": [
1121+
{
1122+
"$ref": "#/components/schemas/InputType"
1123+
}
1124+
],
11211125
"description": "Choose input type: 'path' or 'base64'",
1122-
"default": "path"
1126+
"default": "path",
1127+
"title": "Input Type"
11231128
},
11241129
"description": "Choose input type: 'path' or 'base64'"
11251130
}
@@ -2199,7 +2204,6 @@
21992204
"metadata": {
22002205
"anyOf": [
22012206
{
2202-
"additionalProperties": true,
22032207
"type": "object"
22042208
},
22052209
{

frontend/src/api/api-functions/togglefav.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { APIResponse } from '@/types/API';
44

55
export const togglefav = async (image_id: string): Promise<APIResponse> => {
66
const response = await apiClient.post<APIResponse>(
7-
imagesEndpoints.setfavourite,
7+
imagesEndpoints.setFavourite,
88
{ image_id },
99
);
1010
return response.data;

frontend/src/api/apiEndpoints.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export const imagesEndpoints = {
22
getAllImages: '/images/',
3-
setfavourite: '/images/toggle-favourite',
3+
setFavourite: '/images/toggle-favourite',
44
};
55

66
export const faceClustersEndpoints = {

frontend/src/components/Media/ImageCard.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,15 @@ export function ImageCard({
2525
onClick,
2626
}: ImageCardViewProps) {
2727
const [isImageHovered, setIsImageHovered] = useState(false);
28-
const [isfav, setIsfav] = useState(false);
2928
// Default to empty array if no tags are provided
3029
const tags = image.tags || [];
3130
const { toggleFavourite } = useToggleFav();
3231

33-
const handle_favourite_toggle = () => {
34-
if (!image?.id) return;
35-
toggleFavourite(image?.id);
36-
};
37-
38-
const handleToggleFavorite = useCallback(() => {
39-
if (image) {
40-
setIsfav((prev) => !prev);
41-
handle_favourite_toggle();
32+
const handleToggleFavourite = useCallback(() => {
33+
if (image?.id) {
34+
toggleFavourite(image.id);
4235
}
43-
}, [image, isfav]);
36+
}, [image, toggleFavourite]);
4437
return (
4538
<div
4639
className={cn(
@@ -87,15 +80,15 @@ export function ImageCard({
8780
onClick={(e) => {
8881
console.log(image);
8982
e.stopPropagation();
90-
handleToggleFavorite();
83+
handleToggleFavourite();
9184
}}
9285
>
9386
{image.isFavourite ? (
9487
<Heart className="h-5 w-5" fill="currentColor"></Heart>
9588
) : (
9689
<Heart className="h-5 w-5" />
9790
)}
98-
<span className="sr-only">Favorite</span>
91+
<span className="sr-only">Favourite</span>
9992
</Button>
10093

10194
<Button

frontend/src/components/Media/MediaThumbnails.tsx

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, { useRef, useEffect } from 'react';
2-
import { Heart } from 'lucide-react';
32
import { convertFileSrc } from '@tauri-apps/api/core';
43

54
interface MediaThumbnailsProps {
@@ -11,7 +10,6 @@ interface MediaThumbnailsProps {
1110
currentIndex: number;
1211
showThumbnails: boolean;
1312
onThumbnailClick: (index: number) => void;
14-
favorites: string[];
1513
type?: string;
1614
}
1715

@@ -20,14 +18,12 @@ export const MediaThumbnails: React.FC<MediaThumbnailsProps> = ({
2018
currentIndex,
2119
showThumbnails,
2220
onThumbnailClick,
23-
favorites,
2421
type = 'image',
2522
}) => {
2623
const scrollContainerRef = useRef<HTMLDivElement>(null);
2724
const thumbnailRefs = useRef<Map<number, HTMLDivElement>>(new Map());
2825
const touchStartRef = useRef(0);
2926
const initialScrollLeftRef = useRef(0);
30-
const isFavorite = (imagePath: string) => favorites.includes(imagePath);
3127

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

frontend/src/components/Media/MediaView.tsx

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import type { ImageViewerRef } from './ImageViewer';
1717
// Custom hooks
1818
import { useImageViewControls } from '@/hooks/useImageViewControls';
1919
import { useSlideshow } from '@/hooks/useSlideshow';
20-
import { useFavorites } from '@/hooks/useFavorites';
2120
import { useKeyboardNavigation } from '@/hooks/useKeyboardNavigation';
2221
import { useToggleFav } from '../../hooks/useToggleFav';
2322
import { useLocation } from 'react-router';
@@ -33,8 +32,6 @@ export function MediaView({
3332
const currentViewIndex = useSelector(selectCurrentViewIndex);
3433
const totalImages = images.length;
3534
// guard: images default to empty array in the signature so `images.length` is safe
36-
// keep debug output minimal
37-
// console.log(totalImages);
3835

3936
const currentImage = useMemo(() => {
4037
if (currentViewIndex >= 0 && currentViewIndex < images.length) {
@@ -52,8 +49,6 @@ export function MediaView({
5249

5350
// Custom hooks
5451
const { viewState, handlers } = useImageViewControls();
55-
const { favorites } = useFavorites();
56-
const [isfav, setIsfav] = useState(currentImage?.isFavourite || false);
5752
// Navigation handlers
5853
const handleNextImage = useCallback(() => {
5954
if (currentViewIndex < images.length - 1) {
@@ -84,13 +79,6 @@ export function MediaView({
8479

8580
const location = useLocation();
8681
const { toggleFavourite } = useToggleFav();
87-
// handling toogle_favvvvv
88-
const handle_favourite_toggle = () => {
89-
console.log(location.pathname);
90-
91-
if (!currentImage?.id) return;
92-
toggleFavourite(currentImage?.id);
93-
};
9482

9583
// Slideshow functionality
9684
const { isSlideshowActive, toggleSlideshow } = useSlideshow(
@@ -115,13 +103,14 @@ export function MediaView({
115103
}, []);
116104

117105
// Hooks that depend on currentImage but always declared
118-
const handleToggleFavorite = useCallback(() => {
106+
const handleToggleFavourite = useCallback(() => {
119107
if (currentImage) {
120-
setIsfav((prev) => !prev);
121-
handle_favourite_toggle();
108+
if (currentImage?.id) {
109+
toggleFavourite(currentImage.id);
110+
}
122111
if (location.pathname === '/favourites') handleClose();
123112
}
124-
}, [currentImage, isfav]);
113+
}, [currentImage, toggleFavourite]);
125114

126115
const handleZoomIn = useCallback(() => {
127116
imageViewerRef.current?.zoomIn();
@@ -163,8 +152,8 @@ export function MediaView({
163152
<MediaViewControls
164153
showInfo={showInfo}
165154
onToggleInfo={toggleInfo}
166-
onToggleFavorite={handleToggleFavorite}
167-
isFavorite={isfav}
155+
onToggleFavourite={handleToggleFavourite}
156+
isFavourite={currentImage.isFavourite || false}
168157
onOpenFolder={handleOpenFolder}
169158
isSlideshowActive={isSlideshowActive}
170159
onToggleSlideshow={toggleSlideshow}
@@ -217,7 +206,6 @@ export function MediaView({
217206
currentIndex={currentViewIndex}
218207
showThumbnails={showThumbnails}
219208
onThumbnailClick={handleThumbnailClick}
220-
favorites={favorites}
221209
type={type}
222210
/>
223211
</div>

frontend/src/components/Media/MediaViewControls.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { Info, Folder, Share2, Heart, Play, Pause, X } from 'lucide-react';
44
interface MediaViewControlsProps {
55
showInfo: boolean;
66
onToggleInfo: () => void;
7-
onToggleFavorite: () => void;
7+
onToggleFavourite: () => void;
88
onOpenFolder: () => Promise<void>;
9-
isFavorite: boolean;
9+
isFavourite: boolean;
1010
isSlideshowActive: boolean;
1111
onToggleSlideshow: () => void;
1212
onClose: () => void;
@@ -16,9 +16,9 @@ interface MediaViewControlsProps {
1616
export const MediaViewControls: React.FC<MediaViewControlsProps> = ({
1717
showInfo,
1818
onToggleInfo,
19-
onToggleFavorite,
19+
onToggleFavourite,
2020
onOpenFolder,
21-
isFavorite,
21+
isFavourite,
2222
isSlideshowActive,
2323
onToggleSlideshow,
2424
onClose,
@@ -55,16 +55,18 @@ export const MediaViewControls: React.FC<MediaViewControlsProps> = ({
5555
</button>
5656

5757
<button
58-
onClick={onToggleFavorite}
58+
onClick={onToggleFavourite}
5959
className={`cursor-pointer rounded-full p-2.5 text-white transition-all duration-300 ${
60-
isFavorite
60+
isFavourite
6161
? 'bg-rose-500/80 hover:bg-rose-600 hover:shadow-lg'
6262
: 'bg-black/50 hover:bg-black/20 hover:text-white hover:shadow-lg'
6363
}`}
64-
aria-label={isFavorite ? 'Remove from favorites' : 'Add to favorites'}
65-
title="Favorites"
64+
aria-label={
65+
isFavourite ? 'Remove from favourites' : 'Add to favourites'
66+
}
67+
title="Favourites"
6668
>
67-
<Heart className={`h-5 w-5 ${isFavorite ? 'fill-current' : ''}`} />
69+
<Heart className={`h-5 w-5 ${isFavourite ? 'fill-current' : ''}`} />
6870
</button>
6971

7072
{type === 'image' && (

frontend/src/hooks/useFavorites.ts

Lines changed: 0 additions & 28 deletions
This file was deleted.

frontend/src/hooks/useToggleFav.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const useToggleFav = () => {
99
});
1010
useMutationFeedback(toggleFavouriteMutation, {
1111
showLoading: false,
12+
showSuccess: false,
1213
});
1314
return {
1415
toggleFavourite: (id: any) => toggleFavouriteMutation.mutate(id),

frontend/src/pages/Home/Home.tsx

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ import {
77
import TimelineScrollbar from '@/components/Timeline/TimelineScrollbar';
88
import { Image } from '@/types/Media';
99
import { setImages } from '@/features/imageSlice';
10-
import { showLoader, hideLoader } from '@/features/loaderSlice';
1110
import { selectImages } from '@/features/imageSelectors';
1211
import { usePictoQuery } from '@/hooks/useQueryExtension';
1312
import { fetchAllImages } from '@/api/api-functions';
1413
import { RootState } from '@/app/store';
15-
import { showInfoDialog } from '@/features/infoDialogSlice';
1614
import { EmptyGalleryState } from '@/components/EmptyStates/EmptyGalleryState';
15+
import { useMutationFeedback } from '@/hooks/useMutationFeedback';
1716

1817
export const Home = () => {
1918
const dispatch = useDispatch();
@@ -23,33 +22,28 @@ export const Home = () => {
2322
const searchState = useSelector((state: RootState) => state.search);
2423
const isSearchActive = searchState.active;
2524

26-
const { data, isLoading, isSuccess, isError } = usePictoQuery({
25+
const { data, isLoading, isSuccess, isError, error } = usePictoQuery({
2726
queryKey: ['images'],
2827
queryFn: () => fetchAllImages(),
2928
enabled: !isSearchActive,
3029
});
3130

32-
// Handle fetching lifecycle
31+
useMutationFeedback(
32+
{ isPending: isLoading, isSuccess, isError, error },
33+
{
34+
loadingMessage: 'Loading images',
35+
showSuccess: false,
36+
errorTitle: 'Error',
37+
errorMessage: 'Failed to load images. Please try again later.',
38+
},
39+
);
40+
3341
useEffect(() => {
34-
if (!isSearchActive) {
35-
if (isLoading) {
36-
dispatch(showLoader('Loading images'));
37-
} else if (isError) {
38-
dispatch(hideLoader());
39-
dispatch(
40-
showInfoDialog({
41-
title: 'Error',
42-
message: 'Failed to load images. Please try again later.',
43-
variant: 'error',
44-
}),
45-
);
46-
} else if (isSuccess) {
47-
const images = data?.data as Image[];
48-
dispatch(setImages(images));
49-
dispatch(hideLoader());
50-
}
42+
if (!isSearchActive && isSuccess) {
43+
const images = data?.data as Image[];
44+
dispatch(setImages(images));
5145
}
52-
}, [data, isSuccess, isError, isLoading, dispatch, isSearchActive]);
46+
}, [data, isSuccess, dispatch, isSearchActive]);
5347

5448
const title =
5549
isSearchActive && images.length > 0

0 commit comments

Comments
 (0)