Skip to content

Commit eb3aefd

Browse files
authored
Merge pull request #112 from varun-raj/feat-albums-enhancement
Feat: Albums enhancement and ux enhancements
2 parents 1234f49 + 1132a04 commit eb3aefd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1074
-272
lines changed

bun.lockb

18.2 KB
Binary file not shown.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"dependencies": {
1717
"@google/generative-ai": "^0.21.0",
1818
"@radix-ui/react-accordion": "^1.2.1",
19+
"@radix-ui/react-alert-dialog": "^1.1.4",
1920
"@radix-ui/react-avatar": "^1.1.0",
2021
"@radix-ui/react-checkbox": "^1.1.2",
2122
"@radix-ui/react-dialog": "^1.1.1",
@@ -60,6 +61,7 @@
6061
"react-day-picker": "9.0.8",
6162
"react-dom": "^18",
6263
"react-grid-gallery": "^1.0.1",
64+
"react-hot-toast": "^2.5.1",
6365
"react-leaflet": "^4.2.1",
6466
"react-mentions": "^4.4.10",
6567
"react-query": "^3.39.3",

src/components/albums/AlbumCreateDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export default function AlbumCreateDialog({ onSubmit, assetIds }: IProps) {
5555
return (
5656
<Dialog open={open} onOpenChange={setOpen}>
5757
<DialogTrigger asChild>
58-
<Button>Create Album</Button>
58+
<Button size={"sm"}>Create Album</Button>
5959
</DialogTrigger>
6060
<DialogContent>
6161
<DialogHeader>

src/components/albums/AlbumSelectorDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export default function AlbumSelectorDialog({ onSelected }: IProps) {
7878
return (
7979
<Dialog open={open} onOpenChange={setOpen}>
8080
<DialogTrigger asChild>
81-
<Button>Select Album</Button>
81+
<Button size={"sm"}>Select Album</Button>
8282
</DialogTrigger>
8383
<DialogContent>
8484
<DialogHeader>

src/components/albums/info/AlbumImages.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -125,22 +125,21 @@ export default function AlbumImages({ album }: AlbumImagesProps) {
125125
index={index}
126126
close={() => setIndex(-1)}
127127
/>
128-
<div className="w-full grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 p-2">
128+
<div className="w-full grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-2 p-2">
129129
{images.map((image) => (
130130
<div
131131
key={image.id}
132-
className="w-full h-[200px] overflow-hidden relative"
132+
className="w-full h-[180px] overflow-hidden relative"
133133
>
134134
<LazyImage
135135
loading="lazy"
136136
key={image.id}
137137
src={image.original}
138138
alt={image.originalFileName}
139-
className='overflow-hidden'
139+
className='overflow-hidden max-h-[180px] max-w-[180px] min-h-[180px] min-w-[180px]'
140140
style={{
141141
objectPosition: 'center',
142-
objectFit: 'cover',
143-
height: '100%',
142+
objectFit: 'cover'
144143
}}
145144
onClick={() => handleClick(images.indexOf(image))}
146145
/>

src/components/albums/info/AlbumPeople.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export default function AlbumPeople({ album, onSelect }: AlbumPeopleProps) {
9999
}
100100

101101
return (
102-
<div className="overflow-y-auto min-w-[200px] sticky top-0 py-4 max-h-[calc(100vh-60px)] min-h-[calc(100vh-60px)] dark:bg-zinc-900 bg-gray-200 flex flex-col gap-2 px-2">
102+
<div className="overflow-y-auto min-w-[200px] sticky top-0 py-4 max-h-[calc(100vh-60px)] min-h-[calc(100vh-60px)] border-r border-gray-200 dark:border-zinc-800 flex flex-col gap-2 px-2">
103103

104104
{selectedPerson && (
105105
<div className='flex flex-col gap-2 bg-white dark:bg-zinc-900 p-2 rounded-md'>
@@ -157,7 +157,7 @@ export default function AlbumPeople({ album, onSelect }: AlbumPeopleProps) {
157157
</Button>
158158
)}
159159
{selectedPeople.length > 0 && (
160-
<div className='absolute mx-auto bottom-0 w-full py-2 dark:bg-white bg-black -mx-2 px-2'>
160+
<div className='absolute mx-auto bottom-0 w-full py-2 bg-white darl:bg-black -mx-2 px-2'>
161161
<Button variant="outline" className="!py-0.5 !px-2 text-xs h-7" onClick={handleHideSelectedPeople}>
162162
Hide {selectedPeople.length} people
163163
</Button>

src/components/albums/list/AlbumThumbnail.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
1-
import React, { useMemo } from 'react'
1+
import React, { useMemo, useState } from 'react'
22

33
import Link from 'next/link';
4-
import { useConfig } from '@/contexts/ConfigContext';
54
import { humanizeBytes, humanizeNumber, pluralize } from '@/helpers/string.helper';
65
import LazyImage from '@/components/ui/lazy-image';
76
import { ASSET_THUMBNAIL_PATH } from '@/config/routes';
87
import { IAlbum } from '@/types/album';
9-
import { Badge } from '@/components/ui/badge';
108
import { formatDate } from '@/helpers/date.helper';
119
import { Checkbox } from '@/components/ui/checkbox';
1210
import { differenceInDays } from 'date-fns';
13-
import { FaceIcon } from '@radix-ui/react-icons';
14-
import { Calendar, Camera, Image, User } from 'lucide-react';
11+
import { Calendar, Camera } from 'lucide-react';
1512

1613
interface IAlbumThumbnailProps {
1714
album: IAlbum;
1815
onSelect: (checked: boolean) => void;
16+
selected: boolean;
1917
}
20-
export default function AlbumThumbnail({ album, onSelect }: IAlbumThumbnailProps) {
18+
export default function AlbumThumbnail({ album, onSelect, selected }: IAlbumThumbnailProps) {
19+
const [isSelected, setIsSelected] = useState(selected);
20+
2121
const numberOfDays = useMemo(() => {
2222
return differenceInDays(album.lastPhotoDate, album.firstPhotoDate);
2323
}, [album.firstPhotoDate, album.lastPhotoDate]);
@@ -39,6 +39,7 @@ export default function AlbumThumbnail({ album, onSelect }: IAlbumThumbnailProps
3939
{formatDate(album.firstPhotoDate.toString(), 'MMM d, yyyy')} - {formatDate(album.lastPhotoDate.toString(), 'MMM d, yyyy')}
4040
</div>
4141
<Checkbox
42+
defaultChecked={isSelected}
4243
onCheckedChange={onSelect}
4344
className="absolute top-2 left-2 w-6 h-6 rounded-full border-gray-300 focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
4445
/>

src/components/albums/potential-albums/PotentialAlbumsAssets.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import "yet-another-react-lightbox/styles.css";
22
import { usePotentialAlbumContext } from "@/contexts/PotentialAlbumContext";
33
import { listPotentialAlbumsAssets } from "@/handlers/api/album.handler";
4-
import { IAsset } from "@/types/asset";
5-
import React, { useEffect, useMemo, useState } from "react";
4+
import type { IAsset } from "@/types/asset";
5+
import React, { type MouseEvent, useEffect, useMemo, useState } from "react";
66
import { Gallery } from "react-grid-gallery";
77
import Lightbox, { SlideImage, SlideTypes } from "yet-another-react-lightbox";
88
import Captions from "yet-another-react-lightbox/plugins/captions";
@@ -20,6 +20,7 @@ export default function PotentialAlbumsAssets() {
2020
const [errorMessage, setErrorMessage] = useState<string | null>(null);
2121

2222
const [index, setIndex] = useState(-1);
23+
const [lastSelectedIndex, setLastSelectedIndex] = useState(-1);
2324

2425
const fetchAssets = async () => {
2526
setLoading(true);
@@ -44,14 +45,15 @@ export default function PotentialAlbumsAssets() {
4445
{
4546
title: "Immich Link",
4647
value: (
47-
<a href={exImmichUrl + "/photos/" + p.id} target="_blank">
48+
<a href={`${exImmichUrl}/photos/${p.id}`} target="_blank" rel="noreferrer">
4849
Open in Immich
4950
</a>
5051
),
5152
},
5253
],
5354
}));
54-
}, [assets, selectedIds]);
55+
}, [assets, selectedIds, exImmichUrl]);
56+
5557

5658
const slides = useMemo(
5759
() =>
@@ -73,17 +75,31 @@ export default function PotentialAlbumsAssets() {
7375
[images]
7476
);
7577

76-
const handleClick = (idx: number) => setIndex(idx);
7778

78-
const handleSelect = (_idx: number, asset: IAsset) => {
79+
const handleSelect = (_idx: number, asset: IAsset, event: MouseEvent<HTMLElement>) => {
80+
event.stopPropagation();
7981
const isPresent = selectedIds.includes(asset.id);
8082
if (isPresent) {
8183
updateContext({
8284
selectedIds: selectedIds.filter((id) => id !== asset.id),
8385
});
8486
} else {
85-
updateContext({ selectedIds: [...selectedIds, asset.id] });
87+
const clickedIndex = images.findIndex((image) => {
88+
return image.id === asset.id
89+
});
90+
if (event.shiftKey) {
91+
const startIndex = Math.min(clickedIndex, lastSelectedIndex);
92+
const endIndex = Math.max(clickedIndex, lastSelectedIndex);
93+
const newSelectedIds = images.slice(startIndex, endIndex + 1).map((image) => image.id);
94+
const allSelectedIds = [...selectedIds, ...newSelectedIds];
95+
const uniqueSelectedIds = [...new Set(allSelectedIds)];
96+
updateContext({ selectedIds: uniqueSelectedIds });
97+
} else {
98+
updateContext({ selectedIds: [...selectedIds, asset.id] });
99+
}
100+
setLastSelectedIndex(clickedIndex);
86101
}
102+
87103
};
88104

89105
useEffect(() => {
@@ -121,7 +137,7 @@ export default function PotentialAlbumsAssets() {
121137
<div className="w-full overflow-y-auto max-h-[calc(100vh-60px)]">
122138
<Gallery
123139
images={images}
124-
onClick={handleClick}
140+
onClick={setIndex}
125141
enableImageSelection={true}
126142
onSelect={handleSelect}
127143
thumbnailImageComponent={LazyGridImage}

src/components/albums/potential-albums/PotentialAlbumsDates.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export default function PotentialAlbumsDates() {
4646
}, [filters]);
4747

4848
return (
49-
<div className="overflow-y-auto min-w-[200px] py-4 max-h-[calc(100vh-60px)] min-h-[calc(100vh-60px)] dark:bg-zinc-900 bg-gray-200 flex flex-col gap-2 px-2">
49+
<div className="min-w-[200px] py-4 max-h-[calc(100vh-60px)] min-h-[calc(100vh-60px)] border-r border-gray-200 dark:border-zinc-800 flex flex-col gap-2 px-1">
5050
<div className="flex justify-between items-center gap-2">
5151
<Select
5252
defaultValue={filters.sortBy}
@@ -69,14 +69,15 @@ export default function PotentialAlbumsDates() {
6969
</Button>
7070
</div>
7171
</div>
72-
73-
{dateRecords.map((record) => (
74-
<PotentialDateItem
75-
key={record.date}
76-
record={record}
77-
onSelect={handleSelect}
78-
/>
79-
))}
72+
<div className="overflow-y-auto">
73+
{dateRecords.map((record) => (
74+
<PotentialDateItem
75+
key={record.date}
76+
record={record}
77+
onSelect={handleSelect}
78+
/>
79+
))}
80+
</div>
8081
</div>
8182
);
8283
}

src/components/albums/potential-albums/PotentialDateItem.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ export default function PotentialDateItem({ record, onSelect }: IProps) {
2020
onClick={() => onSelect(record.date)}
2121
key={record.date}
2222
className={
23-
cn("flex gap-1 flex-col p-2 py-1 rounded-lg hover:dark:bg-zinc-800 border border-transparent hover:bg-zinc-100 px-4",
23+
cn("flex gap-1 flex-col p-2 py-1 rounded-lg hover:dark:bg-zinc-800 border border-transparent hover:bg-zinc-100",
2424
startDate === record.date ? "bg-zinc-100 dark:bg-zinc-800 border-gray-300 dark:border-zinc-700" : "")
2525
}
2626
>
27-
<p className="font-mono text-sm">{dateLabel}</p>
27+
<p className="text-sm">{dateLabel}</p>
2828
<p className="text-xs text-foreground/50">{record.asset_count} Orphan Assets</p>
2929
</div>
3030
);

0 commit comments

Comments
 (0)