feat: multi select in potential album and missing location using shift key

Signed-off-by: Varun Raj <varun@skcript.com>
This commit is contained in:
Varun Raj
2025-01-11 11:07:43 +05:30
parent a3e12c8c0d
commit 4820f35d2a
4 changed files with 48 additions and 19 deletions
@@ -1,8 +1,8 @@
import "yet-another-react-lightbox/styles.css";
import { usePotentialAlbumContext } from "@/contexts/PotentialAlbumContext";
import { listPotentialAlbumsAssets } from "@/handlers/api/album.handler";
import { IAsset } from "@/types/asset";
import React, { useEffect, useMemo, useState } from "react";
import type { IAsset } from "@/types/asset";
import React, { type MouseEvent, useEffect, useMemo, useState } from "react";
import { Gallery } from "react-grid-gallery";
import Lightbox, { SlideImage, SlideTypes } from "yet-another-react-lightbox";
import Captions from "yet-another-react-lightbox/plugins/captions";
@@ -20,6 +20,7 @@ export default function PotentialAlbumsAssets() {
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [index, setIndex] = useState(-1);
const [lastSelectedIndex, setLastSelectedIndex] = useState(-1);
const fetchAssets = async () => {
setLoading(true);
@@ -44,14 +45,15 @@ export default function PotentialAlbumsAssets() {
{
title: "Immich Link",
value: (
<a href={exImmichUrl + "/photos/" + p.id} target="_blank">
<a href={`${exImmichUrl}/photos/${p.id}`} target="_blank" rel="noreferrer">
Open in Immich
</a>
),
},
],
}));
}, [assets, selectedIds]);
}, [assets, selectedIds, exImmichUrl]);
const slides = useMemo(
() =>
@@ -73,17 +75,31 @@ export default function PotentialAlbumsAssets() {
[images]
);
const handleClick = (idx: number) => setIndex(idx);
const handleSelect = (_idx: number, asset: IAsset) => {
const handleSelect = (_idx: number, asset: IAsset, event: MouseEvent<HTMLElement>) => {
event.stopPropagation();
const isPresent = selectedIds.includes(asset.id);
if (isPresent) {
updateContext({
selectedIds: selectedIds.filter((id) => id !== asset.id),
});
} else {
updateContext({ selectedIds: [...selectedIds, asset.id] });
const clickedIndex = images.findIndex((image) => {
return image.id === asset.id
});
if (event.shiftKey) {
const startIndex = Math.min(clickedIndex, lastSelectedIndex);
const endIndex = Math.max(clickedIndex, lastSelectedIndex);
const newSelectedIds = images.slice(startIndex, endIndex + 1).map((image) => image.id);
const allSelectedIds = [...selectedIds, ...newSelectedIds];
const uniqueSelectedIds = [...new Set(allSelectedIds)];
updateContext({ selectedIds: uniqueSelectedIds });
} else {
updateContext({ selectedIds: [...selectedIds, asset.id] });
}
setLastSelectedIndex(clickedIndex);
}
};
useEffect(() => {
@@ -121,7 +137,7 @@ export default function PotentialAlbumsAssets() {
<div className="w-full overflow-y-auto max-h-[calc(100vh-60px)]">
<Gallery
images={images}
onClick={handleClick}
onClick={setIndex}
enableImageSelection={true}
onSelect={handleSelect}
thumbnailImageComponent={LazyGridImage}
@@ -2,7 +2,7 @@ import "yet-another-react-lightbox/styles.css";
import { usePotentialAlbumContext } from "@/contexts/PotentialAlbumContext";
import { listPotentialAlbumsAssets } from "@/handlers/api/album.handler";
import { IAsset } from "@/types/asset";
import React, { useEffect, useMemo, useState } from "react";
import React, { useEffect, useMemo, useState, MouseEvent } from "react";
import { Gallery } from "react-grid-gallery";
import Lightbox from "yet-another-react-lightbox";
import Captions from "yet-another-react-lightbox/plugins/captions";
@@ -33,6 +33,7 @@ export default function MissingLocationAssets({ groupBy }: IProps) {
const [errorMessage, setErrorMessage] = useState<string | null>(null);
const [index, setIndex] = useState(-1);
const [lastSelectedIndex, setLastSelectedIndex] = useState(-1);
const fetchAssets = async () => {
if (!startDate && !albumId) return;
@@ -80,14 +81,28 @@ export default function MissingLocationAssets({ groupBy }: IProps) {
const handleClick = (idx: number) => setIndex(idx);
const handleSelect = (_idx: number, asset: IAsset) => {
const handleSelect = (_idx: number, asset: IAsset, event: MouseEvent<HTMLElement>) => {
event.stopPropagation();
const isPresent = selectedIds.includes(asset.id);
if (isPresent) {
updateContext({
selectedIds: selectedIds.filter((id) => id !== asset.id),
});
} else {
updateContext({ selectedIds: [...selectedIds, asset.id] });
const clickedIndex = images.findIndex((image) => {
return image.id === asset.id;
});
if (event.shiftKey) {
const startIndex = Math.min(clickedIndex, lastSelectedIndex);
const endIndex = Math.max(clickedIndex, lastSelectedIndex);
const newSelectedIds = images.slice(startIndex, endIndex + 1).map((image) => image.id);
const allSelectedIds = [...selectedIds, ...newSelectedIds];
const uniqueSelectedIds = [...new Set(allSelectedIds)];
updateContext({ selectedIds: uniqueSelectedIds });
} else {
updateContext({ selectedIds: [...selectedIds, asset.id] });
}
setLastSelectedIndex(clickedIndex);
}
};
@@ -12,7 +12,6 @@ import { IPlace } from "@/types/common";
import React, { useState } from "react";
import TagMissingLocationSearchAndAdd from "./TagMissingLocationSearchAndAdd";
import TagMissingLocationSearchLatLong from "./TagMissingLocationSearchLatLong";
import { MapPinCheck } from "lucide-react";
import dynamic from "next/dynamic";
const LazyMap = dynamic(() => import("./Map"), {
@@ -46,12 +45,11 @@ export default function TagMissingLocationDialog({
Tagging a location will add the location to the selected assets.
</DialogDescription>
</DialogHeader>
<Tabs defaultValue="search" className="border rounded-lg">
<TabsList className="flex justify-between">
<Tabs defaultValue="search">
<TabsList className="flex justify-center">
<TabsTrigger value="search">Search and Pick</TabsTrigger>
<TabsTrigger value="latlong">
Latitude and Longitude
Lat & Long
</TabsTrigger>
<TabsTrigger value="maps">Map</TabsTrigger>
</TabsList>
@@ -63,7 +61,7 @@ export default function TagMissingLocationDialog({
onOpenChange={setOpen} location={mapPosition} onLocationChange={setMapPosition} />
</TabsContent>
<TabsContent value="maps">
<div className="py-10 flex flex-col gap-6 items-center ">
<div className="flex flex-col gap-6 items-center ">
<LazyMap location={mapPosition} onLocationChange={setMapPosition} />
</div>
</TabsContent>
@@ -110,7 +110,7 @@ export default function TagMissingLocationSearchLatLong(
return (
<div className="flex flex-col gap-4 py-4 px-2">
<div className="flex items-start gap-2">
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-2 flex-1">
<Label>Latitude</Label>
<Input
placeholder="Latitude"
@@ -121,7 +121,7 @@ export default function TagMissingLocationSearchLatLong(
onBlur={(e) => handleBlur()}
/>
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-2 flex-1">
<Label>
Longitude
</Label>