diff --git a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx index ebe6870..6fc44f8 100644 --- a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx +++ b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx @@ -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(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: ( - + Open in Immich ), }, ], })); - }, [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) => { + 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() {
(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) => { + 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); } }; diff --git a/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx index d2c60bb..86856cf 100644 --- a/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx +++ b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationDialog.tsx @@ -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. - - + + Search and Pick - Latitude and Longitude - + Lat & Long Map @@ -63,7 +61,7 @@ export default function TagMissingLocationDialog({ onOpenChange={setOpen} location={mapPosition} onLocationChange={setMapPosition} /> -
+
diff --git a/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx index 7c0b8ae..eccee6b 100644 --- a/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx +++ b/src/components/assets/missing-location/TagMissingLocationDialog/TagMissingLocationSearchLatLong.tsx @@ -110,7 +110,7 @@ export default function TagMissingLocationSearchLatLong( return (
-
+
handleBlur()} />
-
+