diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b35c7ea..8972624 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,4 +28,12 @@ You can find the API route here `pages/api/immich-proxy/[...path].ts`
Since the `thumbnailPath` of Immich API doesnt give us a direct link to the image, I've created a proxy api that fetches the image and sends it back as a response.
**For Person's Thumbnail**
-You can find the API route here `pages/api/immich-proxy/thumbnail/[id].ts`
\ No newline at end of file
+You can find the API route here `pages/api/immich-proxy/thumbnail/[id].ts`
+
+### Branching Strategy
+
+- Always create a new branch for your changes
+- Always rebase your branch before creating a PR
+- Always squash your commits before merging
+- Always merge your branch into the `prerelease` branch
+- Always delete your branch after merging
diff --git a/README.md b/README.md
index 92ceefe..932d8bb 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,8 @@ GEMINI_API_KEY= # Gemini API Key
```
Refer here for obtaining Immich API Key: https://immich.app/docs/features/command-line-interface#obtain-the-api-key
+> [!NOTE] When creating the API key, make sure you select all the permissions for the API key.
+
#### Method 2 - Portainer
If you're using portainer, run the docker using `docker run` and add the power tools to the same network as immich.
@@ -149,4 +151,4 @@ Google Gemini 1.5 Flash model is used for parsing your search query in "Find" pa
## Contributing
-Feel free to contribute to this project, I'm open to any suggestions and improvements. Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
\ No newline at end of file
+Feel free to contribute to this project, I'm open to any suggestions and improvements. Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) for more details.
diff --git a/src/components/albums/info/AlbumImages.tsx b/src/components/albums/info/AlbumImages.tsx
index 0736e9f..50918ce 100644
--- a/src/components/albums/info/AlbumImages.tsx
+++ b/src/components/albums/info/AlbumImages.tsx
@@ -1,19 +1,15 @@
-import "yet-another-react-lightbox/styles.css";
import { listAlbumAssets } from "@/handlers/api/album.handler";
import { useConfig } from "@/contexts/ConfigContext";
import { IAlbum, IAlbumPerson } from "@/types/album";
import { IAsset } from "@/types/asset";
-import React, { useEffect, useMemo, useRef, useState, MouseEvent } from "react";
+import React, { useEffect, useMemo, useState } from "react";
import { Hourglass } from "lucide-react";
import { useRouter } from "next/router";
import { Button } from "@/components/ui/button";
-import { Gallery } from "react-grid-gallery";
-import Lightbox from "yet-another-react-lightbox";
-import Captions from "yet-another-react-lightbox/plugins/captions";
-import LazyGridImage from "@/components/ui/lazy-grid-image";
import { usePhotoSelectionContext } from "@/contexts/PhotoSelectionContext";
import { PERSON_THUBNAIL_PATH } from "@/config/routes";
import Image from "next/image";
+import AssetGrid from "@/components/shared/AssetGrid";
interface AlbumImagesProps {
album: IAlbum;
@@ -40,8 +36,6 @@ export default function AlbumImages({ album, selectedPerson }: AlbumImagesProps)
page: 1,
});
const [hasMore, setHasMore] = useState(true);
- const [index, setIndex] = useState(-1);
- const [lastSelectedIndex, setLastSelectedIndex] = useState(-1);
const fetchAssets = async () => {
setLoading(true);
@@ -58,74 +52,24 @@ export default function AlbumImages({ album, selectedPerson }: AlbumImagesProps)
.finally(() => setLoading(false));
};
- const images = useMemo(() => {
- return assets.map((p) => ({
- ...p,
- src: p.url as string,
- original: p.previewUrl as string,
- width: p.exifImageWidth as number,
- height: p.exifImageHeight as number,
- isSelected: selectedIds.includes(p.id),
- orientation: 1,
+ const processedAssets = useMemo(() => {
+ return assets.map((asset) => ({
+ ...asset,
tags: [
{
title: "Immich Link",
value: (
-
+
Open in Immich
),
},
],
}));
- }, [assets, selectedIds, exImmichUrl]);
+ }, [assets, exImmichUrl]);
- const slides = useMemo(
- () =>
- images.map(({ original, width, height }) => ({
- src: original,
- width,
- height,
- })),
- [images]
- );
-
- const handleClick = (idx: number, asset: IAsset, event: MouseEvent) => {
- if (selectedIds.length > 0) {
- handleSelect(idx, asset, event);
- } else {
- setIndex(idx);
- }
- };
-
- 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 {
- const clickedIndex = images.findIndex((image) => {
- return image.id === asset.id;
- });
- if (event.shiftKey && lastSelectedIndex !== -1) {
- const startIndex = Math.min(clickedIndex, lastSelectedIndex);
- const endIndex = Math.max(clickedIndex, lastSelectedIndex);
- if (startIndex >= 0 && endIndex < images.length) {
- const newSelectedIds = images.slice(startIndex, endIndex + 1).map((image) => image.id);
- const allSelectedIds = [...selectedIds, ...newSelectedIds];
- const uniqueSelectedIds = [...new Set(allSelectedIds)];
- updateContext({ selectedIds: uniqueSelectedIds });
- } else {
- console.warn("Shift-select index out of bounds");
- updateContext({ selectedIds: [...selectedIds, asset.id] });
- }
- } else {
- updateContext({ selectedIds: [...selectedIds, asset.id] });
- }
- setLastSelectedIndex(clickedIndex);
- }
+ const handleSelectionChange = (ids: string[]) => {
+ updateContext({ selectedIds: ids });
};
useEffect(() => {
@@ -160,46 +104,28 @@ export default function AlbumImages({ album, selectedPerson }: AlbumImagesProps)
}
return (
- <>
-
- = 0}
- index={index}
- close={() => setIndex(-1)}
+
+
-
- {
+ setFilters({
+ ...filters,
+ page: filters.page + 1,
+ });
}}
- />
- {hasMore && (
-
- )}
-
- >
+ disabled={loading}
+ >
+ {loading ? "Loading..." : "Load More"}
+
+ )}
+
);
}
diff --git a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx
index 548eb80..d0c8159 100644
--- a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx
+++ b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx
@@ -1,15 +1,10 @@
-import "yet-another-react-lightbox/styles.css";
import { usePhotoSelectionContext } from "@/contexts/PhotoSelectionContext";
import { listPotentialAlbumsAssets } from "@/handlers/api/album.handler";
import type { IAsset } from "@/types/asset";
-import React, { type MouseEvent, useEffect, useMemo, useState } from "react";
-import { Gallery } from "react-grid-gallery";
-import Lightbox from "yet-another-react-lightbox";
-import Captions from "yet-another-react-lightbox/plugins/captions";
+import React, { useEffect, useMemo, useState } from "react";
import { CalendarArrowUp, Hourglass } from "lucide-react";
-import Video from "yet-another-react-lightbox/plugins/video";
import { useConfig } from "@/contexts/ConfigContext";
-import LazyGridImage from "@/components/ui/lazy-grid-image";
+import AssetGrid from "@/components/shared/AssetGrid";
export default function PotentialAlbumsAssets() {
const { exImmichUrl } = useConfig();
@@ -24,9 +19,6 @@ export default function PotentialAlbumsAssets() {
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
- const [index, setIndex] = useState(-1);
- const [lastSelectedIndex, setLastSelectedIndex] = useState(-1);
-
const fetchAssets = async () => {
if (!startDate) return;
setLoading(true);
@@ -40,7 +32,7 @@ export default function PotentialAlbumsAssets() {
.finally(() => setLoading(false));
};
- const images = useMemo(() => {
+ const processedAssets = useMemo(() => {
const sortedAssets = [...assets].sort((a, b) => {
try {
return new Date(a.dateTimeOriginal ?? 0).getTime() - new Date(b.dateTimeOriginal ?? 0).getTime();
@@ -49,90 +41,23 @@ export default function PotentialAlbumsAssets() {
}
});
- return sortedAssets.map((p) => ({
- ...p,
- src: p.url as string,
- original: p.previewUrl as string,
- width: p.exifImageWidth as number,
- height: p.exifImageHeight as number,
- isSelected: selectedIds.includes(p.id),
- orientation: 1,
+ return sortedAssets.map((asset) => ({
+ ...asset,
tags: [
{
title: "Immich Link",
value: (
-
+
Open in Immich
),
},
],
}));
- }, [assets, selectedIds, exImmichUrl]);
-
-
- const slides = useMemo(
- () =>
- images.map(({ original, width, height, type, videoURL}) => ({
- src: original,
- width,
- height,
- type: (type === "VIDEO" ? "video" : "image") as any,
- sources:
- type === "VIDEO"
- ? [
- {
- src: videoURL,
- type: "video/mp4",
- },
- ]
- : [],
- })),
- [images]
- );
-
- const handleClick = (idx: number, asset: IAsset, event: MouseEvent) => {
- if (selectedIds.length > 0 && (event.ctrlKey || event.metaKey || event.shiftKey)) {
- handleSelect(idx, asset, event);
- } else {
- setIndex(idx);
- }
- };
-
- const handleSelect = (_idx: number, asset: IAsset, event: MouseEvent) => {
- event.stopPropagation();
- const isPresent = selectedIds.includes(asset.id);
-
- if (isPresent) {
- if (event.ctrlKey || event.metaKey || selectedIds.length === 1) {
- updateContext({
- selectedIds: selectedIds.filter((id) => id !== asset.id),
- });
- } else if (!event.shiftKey) {
- updateContext({ selectedIds: [asset.id] });
- }
- } else {
- const clickedIndex = images.findIndex((image) => image.id === asset.id);
-
- if (event.shiftKey && lastSelectedIndex !== -1) {
- const startIndex = Math.min(clickedIndex, lastSelectedIndex);
- const endIndex = Math.max(clickedIndex, lastSelectedIndex);
- if (startIndex >= 0 && endIndex < images.length) {
- const newSelectedIds = images.slice(startIndex, endIndex + 1).map((image) => image.id);
- const combined = event.ctrlKey || event.metaKey ? [...selectedIds, ...newSelectedIds] : newSelectedIds;
- const uniqueSelectedIds = [...new Set(combined)];
- updateContext({ selectedIds: uniqueSelectedIds });
- } else {
- updateContext({ selectedIds: [...selectedIds, asset.id] });
- }
- } else if (event.ctrlKey || event.metaKey) {
- updateContext({ selectedIds: [...selectedIds, asset.id] });
- } else {
- updateContext({ selectedIds: [asset.id] });
- }
- setLastSelectedIndex(clickedIndex);
- }
+ }, [assets, exImmichUrl]);
+ const handleSelectionChange = (ids: string[]) => {
+ updateContext({ selectedIds: ids });
};
useEffect(() => {
@@ -158,33 +83,15 @@ export default function PotentialAlbumsAssets() {
);
+
return (
- <>
- = 0}
- index={index}
- close={() => setIndex(-1)}
+
);
}
diff --git a/src/components/assets/missing-location/MissingLocationAssets.tsx b/src/components/assets/missing-location/MissingLocationAssets.tsx
index e122784..a60b2b4 100644
--- a/src/components/assets/missing-location/MissingLocationAssets.tsx
+++ b/src/components/assets/missing-location/MissingLocationAssets.tsx
@@ -1,9 +1,5 @@
-import "yet-another-react-lightbox/styles.css";
import { IAsset } from "@/types/asset";
-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";
+import React, { useEffect, useMemo, useState } from "react";
import {
CalendarArrowUp,
Hourglass,
@@ -11,7 +7,7 @@ import {
import { usePhotoSelectionContext } from "@/contexts/PhotoSelectionContext";
import { listMissingLocationAssets } from "@/handlers/api/asset.handler";
import { useConfig } from "@/contexts/ConfigContext";
-import LazyGridImage from "@/components/ui/lazy-grid-image";
+import AssetGrid from "@/components/shared/AssetGrid";
interface IProps {
groupBy: "date" | "album";
@@ -30,9 +26,6 @@ export default function MissingLocationAssets({ groupBy }: IProps) {
const [loading, setLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
- const [index, setIndex] = useState(-1);
- const [lastSelectedIndex, setLastSelectedIndex] = useState(-1);
-
const fetchAssets = async () => {
if (!startDate && !albumId) return;
setLoading(true);
@@ -46,7 +39,7 @@ export default function MissingLocationAssets({ groupBy }: IProps) {
.finally(() => setLoading(false));
};
- const images = useMemo(() => {
+ const processedAssets = useMemo(() => {
let sortedAssets: IAsset[];
try {
if (sortOrder === "desc")
@@ -58,76 +51,23 @@ export default function MissingLocationAssets({ groupBy }: IProps) {
sortedAssets = [...assets];
}
- return sortedAssets.
- map((p) => ({
- ...p,
- src: p.url as string,
- original: p.previewUrl as string,
- width: p.exifImageWidth as number,
- height: p.exifImageHeight as number,
- isSelected: selectedIds.includes(p.id),
- orientation: 1,
- tags: [
- {
- title: "Immich Link",
- value: (
-
- Open in Immich
-
- ),
- },
- ],
- }));
- }, [assets, selectedIds, sortOrder, exImmichUrl]);
+ return sortedAssets.map((asset) => ({
+ ...asset,
+ tags: [
+ {
+ title: "Immich Link",
+ value: (
+
+ Open in Immich
+
+ ),
+ },
+ ],
+ }));
+ }, [assets, sortOrder, exImmichUrl]);
- const slides = useMemo(
- () =>
- images.map(({ original, width, height }) => ({
- src: original,
- width,
- height,
- })),
- [images]
- );
-
- const handleClick = (idx: number, asset: IAsset, event: MouseEvent) => {
- if (selectedIds.length > 0) {
- handleSelect(idx, asset, event);
- }
- else
- {
- setIndex(idx);
- }
- };
-
- 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 {
- const clickedIndex = images.findIndex((image) => {
- return image.id === asset.id;
- });
- if (event.shiftKey && lastSelectedIndex !== -1) {
- const startIndex = Math.min(clickedIndex, lastSelectedIndex);
- const endIndex = Math.max(clickedIndex, lastSelectedIndex);
- if (startIndex >= 0 && endIndex < images.length) {
- const newSelectedIds = images.slice(startIndex, endIndex + 1).map((image) => image.id);
- const allSelectedIds = [...selectedIds, ...newSelectedIds];
- const uniqueSelectedIds = [...new Set(allSelectedIds)];
- updateContext({ selectedIds: uniqueSelectedIds });
- } else {
- console.warn("Shift-select index out of bounds");
- updateContext({ selectedIds: [...selectedIds, asset.id] });
- }
- } else {
- updateContext({ selectedIds: [...selectedIds, asset.id] });
- }
- setLastSelectedIndex(clickedIndex);
- }
+ const handleSelectionChange = (ids: string[]) => {
+ updateContext({ selectedIds: ids });
};
useEffect(() => {
@@ -155,30 +95,13 @@ export default function MissingLocationAssets({ groupBy }: IProps) {
);
return (
- <>
- = 0}
- index={index}
- close={() => setIndex(-1)}
+
);
}