feat: added preview in find

Signed-off-by: Varun Raj <varun@skcript.com>
This commit is contained in:
Varun Raj
2024-12-30 22:02:44 +05:30
parent 9f2f0ef7a6
commit 31e58793be
8 changed files with 98 additions and 70 deletions
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
+1
View File
@@ -2,6 +2,7 @@
const config = { const config = {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {},
}, },
}; };
+5 -11
View File
@@ -1,10 +1,10 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import style from './findInputStyle' import inputStyle from './findInputStyle'
import { MentionsInput, Mention } from 'react-mentions' import { MentionsInput, Mention } from 'react-mentions'
import { searchPeople } from '@/handlers/api/people.handler'; import { searchPeople } from '@/handlers/api/people.handler';
import mentionStyle from './findMentionStyle'; import mentionStyle from './findMentionStyle';
import { PERSON_THUBNAIL_PATH } from '@/config/routes'; import classNames from './FindInput.module.css';
import Image from 'next/image';
interface FindInputProps { interface FindInputProps {
onSearch: (query: string) => void; onSearch: (query: string) => void;
@@ -26,13 +26,8 @@ export default function FindInput({ onSearch }: FindInputProps) {
<MentionsInput <MentionsInput
value={query} value={query}
singleLine={true} singleLine={true}
style={style}
className='w-full border border-gray-200 rounded-md p-2'
classNames={{
control: 'w-full border border-gray-200 rounded-md p-2',
input: 'w-full border border-gray-200 rounded-md p-2 w-full',
}}
placeholder='Search for photos, use @ to search for people' placeholder='Search for photos, use @ to search for people'
style={inputStyle}
onKeyDown={(e) => { onKeyDown={(e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
onSearch(query); onSearch(query);
@@ -41,14 +36,13 @@ export default function FindInput({ onSearch }: FindInputProps) {
onChange={(e) => setQuery(e.target.value)}> onChange={(e) => setQuery(e.target.value)}>
<Mention <Mention
trigger="@" trigger="@"
data={handleSearchPeople}
style={mentionStyle} style={mentionStyle}
data={handleSearchPeople}
renderSuggestion={( renderSuggestion={(
highlightedDisplay, highlightedDisplay,
focused focused
) => ( ) => (
<div className={`user ${focused ? 'focused' : ''} `}> <div className={`user ${focused ? 'focused' : ''} `}>
{highlightedDisplay.display} {highlightedDisplay.display}
</div> </div>
)} )}
+19 -14
View File
@@ -1,37 +1,42 @@
const findInputStyle = { const findInputStyle = {
control: { control: {
backgroundColor: '#fff', backgroundColor: 'hsl(var(--input))',
fontSize: 14, fontSize: 20,
fontWeight: 'normal', fontWeight: 'normal',
}, outline: 'none',
borderRadius: '15px',
},
'&singleLine': { '&singleLine': {
display: 'inline-block', // display: 'inline-block',
width: '100%',
highlighter: { highlighter: {
padding: '10px', padding: '10px 15px',
border: '2px inset transparent', border: 'none',
outline: 'none',
}, },
input: { input: {
width: "100%", padding: '10px 15px',
padding: '10px', border: 'none',
borderRadius: '4px', outline: 'none',
border: '1px solid #e0e0e0',
}, },
}, },
suggestions: { suggestions: {
list: { list: {
backgroundColor: 'white', backgroundColor: 'hsl(var(--background))',
border: '1px solid rgba(0,0,0,0.15)',
fontSize: 14, fontSize: 14,
border: '1px solid hsl(var(--border))',
overflow: 'hidden',
}, },
item: { item: {
padding: '5px 15px', padding: '5px 15px',
borderBottom: '1px solid rgba(0,0,0,0.15)', border: 'none',
'&focused': { '&focused': {
backgroundColor: '#cee4e5', backgroundColor: 'hsl(var(--chart-2))',
}, },
}, },
}, },
+2 -1
View File
@@ -1,3 +1,4 @@
export default { export default {
backgroundColor: '#cee4e5', backgroundColor: 'hsla(var(--chart-2) / 0.3)',
position: 'relative',
} }
+2 -2
View File
@@ -23,11 +23,11 @@ export default function PageLayout({ children, className, title }: IProps) {
: children; : children;
return ( return (
<div> <div className="flex flex-col w-full h-full">
{header} {header}
<main <main
className={cn( className={cn(
"flex flex-1 max-h-[calc(100vh-60px)] flex-col gap-4 overflow-y-auto mx-auto", "flex flex-1 max-h-[calc(100vh-60px)] flex-col gap-4 overflow-y-auto mx-auto w-full",
{ {
"mb-14": !!header, "mb-14": !!header,
}, },
+44 -11
View File
@@ -1,22 +1,39 @@
import "yet-another-react-lightbox/styles.css";
import FindInput from '@/components/find/FindInput'; import FindInput from '@/components/find/FindInput';
import PageLayout from '@/components/layouts/PageLayout'; import PageLayout from '@/components/layouts/PageLayout';
import Header from '@/components/shared/Header'; import Header from '@/components/shared/Header';
import Loader from '@/components/ui/loader'; import Loader from '@/components/ui/loader';
import { ASSET_THUMBNAIL_PATH } from '@/config/routes'; import { ASSET_PREVIEW_PATH, ASSET_THUMBNAIL_PATH } from '@/config/routes';
import { useConfig } from '@/contexts/ConfigContext'; import { useConfig } from '@/contexts/ConfigContext';
import { findAssets } from '@/handlers/api/asset.handler'; import { findAssets } from '@/handlers/api/asset.handler';
import { IAsset } from '@/types/asset'; import { IAsset } from '@/types/asset';
import { Search, TriangleAlert, WandSparkles } from 'lucide-react'; import { Captions, Megaphone, Search, Speaker, TriangleAlert, WandSparkles } from 'lucide-react';
import Image from 'next/image'; import Image from 'next/image';
import React, { useState } from 'react' import React, { useMemo, useState } from 'react'
import Lightbox from 'yet-another-react-lightbox';
import { Button } from "@/components/ui/button";
import Link from "next/link";
export default function FindPage() { export default function FindPage() {
const { geminiEnabled } = useConfig();
const [index, setIndex] = useState(-1);
const { geminiEnabled, exImmichUrl } = useConfig();
const [query, setQuery] = useState(''); const [query, setQuery] = useState('');
const [assets, setAssets] = useState<IAsset[]>([]); const [assets, setAssets] = useState<IAsset[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const slides = useMemo(
() =>
assets.map((asset) => ({
src: ASSET_PREVIEW_PATH(asset.id),
width: 1000,
height: 1000,
})),
[assets]
);
const handleSearch = (query: string) => { const handleSearch = (query: string) => {
setQuery(query); setQuery(query);
setLoading(true); setLoading(true);
@@ -42,6 +59,9 @@ export default function FindPage() {
<p className='text-sm text-muted-foreground'> <p className='text-sm text-muted-foreground'>
Example: Sunset photos from last week. Use <kbd className='bg-zinc-200 text-black dark:text-white px-1 py-0.5 rounded-md dark:bg-zinc-500'>@</kbd> to search for photos of a specific person. Example: Sunset photos from last week. Use <kbd className='bg-zinc-200 text-black dark:text-white px-1 py-0.5 rounded-md dark:bg-zinc-500'>@</kbd> to search for photos of a specific person.
</p> </p>
<p className='text-xs text-muted-foreground text-center flex gap-1 items-center'>
<span>Power tools uses Google Gemini only for parsing your query. None of your data is sent to Gemini.</span>
</p>
</div> </div>
} }
else if (assets.length === 0) { else if (assets.length === 0) {
@@ -50,9 +70,21 @@ export default function FindPage() {
</div> </div>
} }
return ( return (
<>
<Lightbox
slides={slides}
open={index >= 0}
index={index}
close={() => setIndex(-1)}
/>
<div className="grid grid-cols-5 gap-4 p-2"> <div className="grid grid-cols-5 gap-4 p-2">
{assets.map((asset) => ( {assets.map((asset, idx) => (
<div key={asset.id} className='w-full h-[200px] rounded-md overflow-hidden'> <div key={asset.id} className='w-full h-[200px] rounded-md overflow-hidden relative'>
<Link href={`${exImmichUrl}/photos/${asset.id}`}
target="_blank"
className='absolute top-2 right-2 text-xs'>
Open in Immich
</Link>
<Image <Image
src={ASSET_THUMBNAIL_PATH(asset.id)} src={ASSET_THUMBNAIL_PATH(asset.id)}
alt={asset.id} alt={asset.id}
@@ -63,10 +95,12 @@ export default function FindPage() {
}} }}
width={200} width={200}
height={200} height={200}
onClick={() => setIndex(idx)}
/> />
</div> </div>
))} ))}
</div> </div>
</>
) )
} }
@@ -74,12 +108,12 @@ export default function FindPage() {
<PageLayout> <PageLayout>
<Header leftComponent="Find" /> <Header leftComponent="Find" />
{geminiEnabled ? ( {geminiEnabled ? (
<div> <>
<div className="flex flex-col gap-4 p-2"> <div className="flex flex-col gap-4 p-2">
<FindInput onSearch={handleSearch} /> <FindInput onSearch={handleSearch} />
</div> </div>
{renderContent()} {renderContent()}
</div> </>
) : ( ) : (
<div className="flex justify-center py-10 items-center h-full flex-col gap-2"> <div className="flex justify-center py-10 items-center h-full flex-col gap-2">
<WandSparkles className='w-10 h-10 text-muted-foreground' /> <WandSparkles className='w-10 h-10 text-muted-foreground' />
@@ -89,9 +123,8 @@ export default function FindPage() {
Please enable Gemini by adding <kbd className='bg-zinc-200 text-black dark:text-white px-1 py-0.5 rounded-md dark:bg-zinc-500'>GEMINI_API_KEY</kbd> in the <kbd className='bg-zinc-200 text-black dark:text-white px-1 py-0.5 rounded-md dark:bg-zinc-500'>.env</kbd> file. Please enable Gemini by adding <kbd className='bg-zinc-200 text-black dark:text-white px-1 py-0.5 rounded-md dark:bg-zinc-500'>GEMINI_API_KEY</kbd> in the <kbd className='bg-zinc-200 text-black dark:text-white px-1 py-0.5 rounded-md dark:bg-zinc-500'>.env</kbd> file.
</p> </p>
<div className="border border-l-4 border-zinc-200 dark:border-zinc-500 rounded-md p-2"> <div className="border border-l-4 border-zinc-200 dark:border-zinc-500 rounded-md p-2">
<p className='text-sm text-muted-foreground text-center flex gap-1 items-center'> <p className='text-xs text-muted-foreground text-center flex gap-1 items-center'>
<TriangleAlert className='w-4 h-4' /> <span>Power tools uses Google Gemini only for parsing your query. None of your data is sent to Gemini.</span>
<span>Power tools uses Gemini only for parsing your query. None of your data is sent to Gemini.</span>
</p> </p>
</div> </div>
</div> </div>
+1 -1
View File
@@ -31,7 +31,6 @@
--chart-3: 197 37% 24%; --chart-3: 197 37% 24%;
--chart-4: 43 74% 66%; --chart-4: 43 74% 66%;
--chart-5: 27 87% 67%; --chart-5: 27 87% 67%;
} }
.dark { .dark {
@@ -88,3 +87,4 @@
--chart-5: 340 75% 55%; --chart-5: 340 75% 55%;
} }
} }