Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/select images by multiple tags 197 #203

Merged
19 changes: 10 additions & 9 deletions frontend/src/components/AITagging/AIgallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,14 @@ export default function AIGallery({
queryFn: getAllImageObjects,
queryKey: ['ai-tagging-images', 'ai'],
});

const { mutate: generateThumbnail, isPending: isCreating } = usePictoMutation(
{
mutationFn: generateThumbnails,
autoInvalidateTags: ['ai-tagging-images', 'ai'],
},
);

const [filterTag, setFilterTag] = useState<string>('');
let mediaItems = successData ?? [];
const [filterTag, setFilterTag] = useState<string[]>([]);
const [currentPage, setCurrentPage] = useState<number>(1);
const [showMediaViewer, setShowMediaViewer] = useState<boolean>(false);
const [selectedMediaIndex, setSelectedMediaIndex] = useState<number>(0);
Expand All @@ -55,14 +54,17 @@ export default function AIGallery({
{ length: 41 },
(_, index) => index + 10,
);

const filteredMediaItems = useMemo(() => {
return filterTag
const filteredMediaItems = useMemo(() => {
return filterTag.length > 0
? mediaItems.filter((mediaItem: any) =>
mediaItem.tags?.includes(filterTag),
filterTag.some((tag) => mediaItem.tags.includes(tag))
)
: mediaItems;
}, [filterTag, mediaItems]);
}, [filterTag, mediaItems, loading])


const [pageNo,setpageNo] = useState<number>(20);


const currentItems = useMemo(() => {
const indexOfLastItem = currentPage * pageNo;
Expand Down Expand Up @@ -105,7 +107,6 @@ export default function AIGallery({
<h1 className="text-2xl font-bold">{title}</h1>
)}
<FilterControls
filterTag={filterTag}
setFilterTag={setFilterTag}
mediaItems={mediaItems}
onFolderAdded={handleFolderAdded}
Expand Down
97 changes: 82 additions & 15 deletions frontend/src/components/AITagging/FilterControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import FolderPicker from '../FolderPicker/FolderPicker';
import LoadingScreen from '../ui/LoadingScreen/LoadingScreen';
import DeleteSelectedImagePage from '../FolderPicker/DeleteSelectedImagePage';
import ErrorDialog from '../Album/Error';
import { Trash2, Filter } from 'lucide-react';
import {
Trash2,
Filter,
} from 'lucide-react';
import { queryClient, usePictoMutation } from '@/hooks/useQueryExtensio';
import { addFolder } from '../../../api/api-functions/images';

interface FilterControlsProps {
filterTag: string;
setFilterTag: (tag: string) => void;
setFilterTag: (tag: string[]) => void;
mediaItems: MediaItem[];
onFolderAdded: () => Promise<void>;
isLoading: boolean;
Expand All @@ -27,7 +30,6 @@ interface FilterControlsProps {
}

export default function FilterControls({
filterTag,
setFilterTag,
mediaItems,
onFolderAdded,
Expand All @@ -54,6 +56,46 @@ export default function FilterControls({
.sort();
}, [mediaItems]);

const [isDropdownOpen, setIsDropdownOpen] = useState<boolean>(false);

const [selectedFlags, setSelectedFlags] = useState<
{ tag: string; isChecked: boolean }[]
>([
{ tag: 'All tags', isChecked: false },
...uniqueTags.map((ele) => ({ tag: ele, isChecked: false })),
]);

const handleAddFlag = (idx: number) => {
const updatedFlags = [...selectedFlags];
updatedFlags[idx].isChecked = true;
setSelectedFlags(updatedFlags);
};

const handleRemoveFlag = (idx: number) => {
const updatedFlags = [...selectedFlags];
updatedFlags[idx].isChecked = false;
setSelectedFlags(updatedFlags);
};


const handleFilterFlag = ()=>{
let flags : string[] = [];
if(selectedFlags[0].isChecked) {
setFilterTag([]);
return;
}
selectedFlags.forEach((ele)=>{
if(ele.isChecked) flags.push(ele.tag);
})

console.log("Updated Filter Flags = ",flags);
setFilterTag(flags);
}

const handleToggleDropdown = (event:Event) => {
event.preventDefault();
setIsDropdownOpen((prevState) => !prevState); // Toggle dropdown visibility
};
const handleFolderPick = async (path: string) => {
try {
addFolderAPI(path);
Expand Down Expand Up @@ -106,16 +148,28 @@ export default function FilterControls({
<Trash2 className="h-4 w-4" />
<p className="ml-1 hidden lg:inline">Delete Images</p>
</Button>
<DropdownMenu>
<DropdownMenu open={isDropdownOpen} onOpenChange={setIsDropdownOpen}>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="flex items-center gap-2 border-gray-500 hover:bg-accent dark:hover:bg-white/10"
className="flex cursor-default items-center gap-2 border-gray-500 p-2 hover:bg-accent dark:hover:bg-white/10"
onClick={()=>handleToggleDropdown}
>
<Filter className="h-4 w-4" />
<p className="hidden lg:inline">
Filter by {filterTag || 'tags'}
</p>
Filter by{' '}
<div className="flex gap-2">
{selectedFlags.map((ele, idx) =>
ele.isChecked ? (
<p
key={idx}
className="flex items-center justify-center gap-1 rounded-lg border-white bg-gray-800 pb-1 pl-2 pr-2 pt-1"
>
{ele.tag}

</p>
) : null,
)}
</div>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
Expand All @@ -124,18 +178,31 @@ export default function FilterControls({
>
<DropdownMenuRadioGroup
className="overflow-auto"
value={filterTag}
onValueChange={setFilterTag}
onValueChange={handleFilterFlag}
>
<DropdownMenuRadioItem value="">All tags</DropdownMenuRadioItem>
{uniqueTags.map((tag) => (
<DropdownMenuRadioItem key={tag} value={tag}>
{tag}
{selectedFlags.map((ele, index) => (
<DropdownMenuRadioItem
key={ele.tag}
value={ele.tag}
onSelect={(event) => {
selectedFlags[index].isChecked ? handleRemoveFlag(index) : handleAddFlag(index);
event.preventDefault();
}}
className='cursor-pointer'
>
<input
type="checkbox"
className="mr-2 cursor-pointer"
value={ele.tag}
checked={selectedFlags[index].isChecked}
/>
{ele.tag}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>

<ErrorDialog
content={errorDialogContent}
onClose={() => setErrorDialogContent(null)}
Expand Down
65 changes: 56 additions & 9 deletions frontend/src/components/Media/MediaGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import SortingControls from './SortningControls';
import PaginationControls from '../ui/PaginationControls';
import { MediaGalleryProps } from '@/types/Media';
import { sortMedia } from '@/utils/Media';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from '@radix-ui/react-dropdown-menu';
import { Button } from '../ui/button';

export default function MediaGallery({
mediaItems,
Expand All @@ -19,17 +27,22 @@ export default function MediaGallery({
const itemsPerPage: number = 20;
const itemsPerRow: number = 3;

const noOfPages: number[] = Array.from(
{ length: 41 },
(_, index) => index + 10,
);
const [pageNo, setpageNo] = useState<number>(20);
const sortedMedia = useMemo(() => {
return sortMedia(mediaItems, [sortBy]);
}, [mediaItems, sortBy]);

const currentItems = useMemo(() => {
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const indexOfLastItem = currentPage * pageNo;
const indexOfFirstItem = indexOfLastItem - pageNo;
return sortedMedia.slice(indexOfFirstItem, indexOfLastItem);
}, [sortedMedia, currentPage, itemsPerPage]);
}, [sortedMedia, currentPage, pageNo]);

const totalPages = Math.ceil(sortedMedia.length / itemsPerPage);
const totalPages = Math.ceil(sortedMedia.length / pageNo);

const handleSetSortBy = useCallback((value: string) => {
setSortBy(value);
Expand Down Expand Up @@ -61,11 +74,45 @@ export default function MediaGallery({
type={type}
/>
{totalPages >= 1 && (
<PaginationControls
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
/>
<div className='relative flex items-center justify-center gap-4'>
<PaginationControls
currentPage={currentPage}
totalPages={totalPages}
onPageChange={setCurrentPage}
/>
<div className="absolute right-0 mt-5">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
className="flex items-center gap-2 border-gray-500 hover:bg-accent dark:hover:bg-white/10"
>
<p className="hidden lg:inline">
Num of images per page : {pageNo}
</p>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
className="max-h-[500px] w-[200px] overflow-y-auto"
align="end"
>
<DropdownMenuRadioGroup
className="cursor-pointer overflow-auto bg-gray-950 p-4"
onValueChange={(value) => setpageNo(Number(value))}
>
{noOfPages.map((itemsPerPage) => (
<DropdownMenuRadioItem
key={itemsPerPage}
value={`${itemsPerPage}`}
>
{itemsPerPage}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
)}
{showMediaViewer && (
<MediaView
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/hooks/useImages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ interface ImageData {
title: string;
date: string;
tags: string[];
imagePath: string;
imagePath:string;
}


interface ResponseData {
[year: string]: {
[month: string]: string[];
Expand Down Expand Up @@ -111,6 +112,7 @@ export const useImages = (folderPath: string) => {
title: `Image ${imagePath}`,
date,
tags: [],
imagePath:''
};
}),
);
Expand Down