diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a19a850..a6ef46e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -43,7 +43,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push Docker image run: | - docker build --build-arg VERSION=${{ needs.release.outputs.new-release-version }} \ + docker build --build-arg BUILD_VERSION=${{ needs.release.outputs.new-release-version }} VERSION=${{ needs.release.outputs.new-release-version }} \ -t ghcr.io/${{ github.repository }}:${{ needs.release.outputs.new-release-version }} \ -t ghcr.io/${{ github.repository }}:latest . docker push ghcr.io/${{ github.repository }}:latest diff --git a/bun.lockb b/bun.lockb index 5a977a2..a8bbfdd 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/docker-compose.build.yml b/docker-compose.build.yml index 23e85e3..3c431eb 100644 --- a/docker-compose.build.yml +++ b/docker-compose.build.yml @@ -10,3 +10,5 @@ services: - "3000:3000" env_file: - .env + environment: + - BUILD_VERSION=${BUILD_VERSION} diff --git a/next.config.mjs b/next.config.mjs index 9685eaf..ee15efb 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,10 +1,20 @@ /** @type {import('next').NextConfig} */ + +import { readFileSync } from 'fs'; + +const { version } = JSON.parse(readFileSync('./package.json', 'utf-8')); + +console.log(`Version: ${version}`); + const nextConfig = { reactStrictMode: false, output: 'standalone', images: { unoptimized: true, - } + }, + env: { + VERSION: process.env.BUILD_VERSION || version, + }, }; export default nextConfig; diff --git a/package.json b/package.json index 7ca0bcd..7cf455a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "immich-power-tools", - "version": "0.5.0", + "version": "0.6.1", "private": true, "scripts": { "dev": "next dev", @@ -33,6 +33,7 @@ "@types/qs": "^6.9.15", "@types/react-calendar-heatmap": "^1.6.7", "axios": "^1.7.4", + "chrono-node": "^1.4.9", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "1.0.0", @@ -49,6 +50,7 @@ "react-day-picker": "9.0.8", "react-dom": "^18", "react-grid-gallery": "^1.0.1", + "react-query": "^3.39.3", "recharts": "^2.12.7", "sass": "^1.77.8", "tailwind-merge": "^2.5.2", diff --git a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx index c0d0d2d..2818863 100644 --- a/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx +++ b/src/components/albums/potential-albums/PotentialAlbumsAssets.tsx @@ -8,8 +8,11 @@ import Lightbox, { SlideImage, SlideTypes } from "yet-another-react-lightbox"; import Captions from "yet-another-react-lightbox/plugins/captions"; import { CalendarArrowDown, CalendarArrowUp, Hourglass } from "lucide-react"; import Video from "yet-another-react-lightbox/plugins/video"; +import { useConfig } from "@/contexts/ConfigContext"; +import LazyImage from "@/components/ui/lazy-image"; export default function PotentialAlbumsAssets() { + const { exImmichUrl } = useConfig(); const { startDate, selectedIds, assets, updateContext } = usePotentialAlbumContext(); @@ -37,12 +40,22 @@ export default function PotentialAlbumsAssets() { width: p.exifImageWidth as number, height: p.exifImageHeight as number, isSelected: selectedIds.includes(p.id), + tags: [ + { + title: "Immich Link", + value: ( + + Open in Immich + + ), + }, + ], })); }, [assets, selectedIds]); const slides = useMemo( () => - images.map(({ original, width, height, type, videoURL }) => ({ + images.map(({ original, width, height, type, videoURL}) => ({ src: original, width, height, @@ -111,6 +124,14 @@ export default function PotentialAlbumsAssets() { onClick={handleClick} enableImageSelection={true} onSelect={handleSelect} + thumbnailImageComponent={LazyImage} + tagStyle={{ + color: "white", + fontSize: "12px", + backgroundColor: "rgba(0, 0, 0)", + padding: "2px", + borderRadius: "5px", + }} /> diff --git a/src/components/albums/potential-albums/PotentialAlbumsDates.tsx b/src/components/albums/potential-albums/PotentialAlbumsDates.tsx index 03a428c..31c537e 100644 --- a/src/components/albums/potential-albums/PotentialAlbumsDates.tsx +++ b/src/components/albums/potential-albums/PotentialAlbumsDates.tsx @@ -5,8 +5,10 @@ import { import React, { use, useEffect, useState } from "react"; import PotentialDateItem from "./PotentialDateItem"; import { usePotentialAlbumContext } from "@/contexts/PotentialAlbumContext"; +import { useRouter } from "next/router"; export default function PotentialAlbumsDates() { + const router = useRouter(); const { updateContext } = usePotentialAlbumContext(); const [dateRecords, setDateRecords] = React.useState< IPotentialAlbumsDatesResponse[] @@ -25,6 +27,11 @@ export default function PotentialAlbumsDates() { const handleSelect = (date: string) => { updateContext({ startDate: date }); + router.push({ + pathname: router.pathname, + query: { startDate: date }, + }) + }; useEffect(() => { diff --git a/src/components/assets/missing-location/MissingLocationAssets.tsx b/src/components/assets/missing-location/MissingLocationAssets.tsx index 7acda77..6b58ace 100644 --- a/src/components/assets/missing-location/MissingLocationAssets.tsx +++ b/src/components/assets/missing-location/MissingLocationAssets.tsx @@ -18,6 +18,7 @@ import { listMissingLocationAssets } from "@/handlers/api/asset.handler"; import { formatDate, parseDate } from "@/helpers/date.helper"; import { addDays } from "date-fns"; import { useConfig } from "@/contexts/ConfigContext"; +import LazyImage from "@/components/ui/lazy-image"; export default function MissingLocationAssets() { const { exImmichUrl } = useConfig(); @@ -125,6 +126,7 @@ export default function MissingLocationAssets() { onClick={handleClick} enableImageSelection={true} onSelect={handleSelect} + thumbnailImageComponent={LazyImage} tagStyle={{ color: "white", fontSize: "12px", diff --git a/src/components/assets/missing-location/MissingLocationDates.tsx b/src/components/assets/missing-location/MissingLocationDates.tsx index 8af86ec..41f6216 100644 --- a/src/components/assets/missing-location/MissingLocationDates.tsx +++ b/src/components/assets/missing-location/MissingLocationDates.tsx @@ -3,13 +3,14 @@ import { usePotentialAlbumContext } from "@/contexts/PotentialAlbumContext"; import { IMissingLocationDatesResponse, listMissingLocationDates } from "@/handlers/api/asset.handler"; import MissingLocationDateItem from "./MissingLocationDateItem"; import { useMissingLocationContext } from "@/contexts/MissingLocationContext"; +import { useRouter } from "next/router"; export default function MissingLocationDates() { const { updateContext } = useMissingLocationContext(); + const router = useRouter(); const [dateRecords, setDateRecords] = React.useState< IMissingLocationDatesResponse[] >([]); - const [loading, setLoading] = useState(false); const [errorMessage, setErrorMessage] = useState(null); @@ -23,6 +24,10 @@ export default function MissingLocationDates() { const handleSelect = (date: string) => { updateContext({ startDate: date }); + router.push({ + pathname: router.pathname, + query: { startDate: date }, + }); }; useEffect(() => { diff --git a/src/components/layouts/RootLayout.tsx b/src/components/layouts/RootLayout.tsx index 91ff2b5..3eab240 100644 --- a/src/components/layouts/RootLayout.tsx +++ b/src/components/layouts/RootLayout.tsx @@ -10,11 +10,14 @@ import { ArrowRight, TriangleAlert } from "lucide-react"; import Link from "next/link"; import { LoginForm } from "../auth/LoginForm"; import { useConfig } from "@/contexts/ConfigContext"; +import { queryClient } from "@/config/rQuery"; +import { QueryClientProvider } from "react-query"; type RootLayoutProps = { children: ReactNode; }; + export default function RootLayout({ children }: RootLayoutProps) { const { immichURL, exImmichUrl } = useConfig(); const [user, setUser] = useState(null); @@ -34,9 +37,7 @@ export default function RootLayout({ children }: RootLayoutProps) { .finally(() => setLoading(false)); }; - const handleLoginCompletion = (user: IUser) => { - setUser(user); - } + const handleLoginCompletion = (user: IUser) => setUser(user); useEffect(() => { fetchData(); @@ -86,15 +87,17 @@ export default function RootLayout({ children }: RootLayoutProps) { if (!user) return ; return ( - -
- -
{children}
- -
-
+ + +
+ +
{children}
+ +
+
+
); } diff --git a/src/components/people/PeopleFilters.tsx b/src/components/people/PeopleFilters.tsx index c6f7ffe..232ba81 100644 --- a/src/components/people/PeopleFilters.tsx +++ b/src/components/people/PeopleFilters.tsx @@ -13,11 +13,24 @@ import { DropdownMenuTrigger, } from "../ui/dropdown-menu"; import { Input } from "../ui/input"; +import { IPersonListFilters } from "@/handlers/api/people.handler"; +import AssetFilter from "../shared/common/AssetFilter"; export function PeopleFilters() { const router = useRouter(); const { updateContext, page, maximumAssetCount } = usePeopleFilterContext(); + const handleChange = (data: Partial) => { + updateContext(data); + router.push({ + pathname: router.pathname, + query: { + ...router.query, + ...data, + page: undefined, + }, + }); + } const [nextPage, prevPage] = useMemo(() => { const pageNum = parseInt(page.toString() || "1", 10); return [pageNum + 1, pageNum - 1]; @@ -32,9 +45,9 @@ export function PeopleFilters() { onChange={(e) => { try { const value = parseInt(e.target.value || "0", 10); - updateContext({ maximumAssetCount: value }); + handleChange({ maximumAssetCount: value }); } catch (e) { - updateContext({ maximumAssetCount: 0 }); + handleChange({ maximumAssetCount: 0 }); } }} /> @@ -42,7 +55,7 @@ export function PeopleFilters() { { - updateContext({ nameLessOnly: checked, page: 1 }); + handleChange({ nameLessOnly: checked, page: 1 }); }} />