feat: added preview in find
Signed-off-by: Varun Raj <varun@skcript.com>
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useState } from 'react'
|
||||
import style from './findInputStyle'
|
||||
import inputStyle from './findInputStyle'
|
||||
import { MentionsInput, Mention } from 'react-mentions'
|
||||
import { searchPeople } from '@/handlers/api/people.handler';
|
||||
import mentionStyle from './findMentionStyle';
|
||||
import { PERSON_THUBNAIL_PATH } from '@/config/routes';
|
||||
import Image from 'next/image';
|
||||
import classNames from './FindInput.module.css';
|
||||
|
||||
|
||||
interface FindInputProps {
|
||||
onSearch: (query: string) => void;
|
||||
@@ -26,13 +26,8 @@ export default function FindInput({ onSearch }: FindInputProps) {
|
||||
<MentionsInput
|
||||
value={query}
|
||||
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'
|
||||
style={inputStyle}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
onSearch(query);
|
||||
@@ -41,14 +36,13 @@ export default function FindInput({ onSearch }: FindInputProps) {
|
||||
onChange={(e) => setQuery(e.target.value)}>
|
||||
<Mention
|
||||
trigger="@"
|
||||
data={handleSearchPeople}
|
||||
style={mentionStyle}
|
||||
data={handleSearchPeople}
|
||||
renderSuggestion={(
|
||||
highlightedDisplay,
|
||||
focused
|
||||
) => (
|
||||
<div className={`user ${focused ? 'focused' : ''}`}>
|
||||
|
||||
<div className={`user ${focused ? 'focused' : ''} `}>
|
||||
{highlightedDisplay.display}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,37 +1,42 @@
|
||||
const findInputStyle = {
|
||||
control: {
|
||||
backgroundColor: '#fff',
|
||||
fontSize: 14,
|
||||
backgroundColor: 'hsl(var(--input))',
|
||||
fontSize: 20,
|
||||
fontWeight: 'normal',
|
||||
outline: 'none',
|
||||
borderRadius: '15px',
|
||||
|
||||
},
|
||||
|
||||
|
||||
'&singleLine': {
|
||||
display: 'inline-block',
|
||||
// display: 'inline-block',
|
||||
width: '100%',
|
||||
|
||||
highlighter: {
|
||||
padding: '10px',
|
||||
border: '2px inset transparent',
|
||||
padding: '10px 15px',
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
},
|
||||
input: {
|
||||
width: "100%",
|
||||
padding: '10px',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #e0e0e0',
|
||||
padding: '10px 15px',
|
||||
border: 'none',
|
||||
outline: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
suggestions: {
|
||||
|
||||
list: {
|
||||
backgroundColor: 'white',
|
||||
border: '1px solid rgba(0,0,0,0.15)',
|
||||
backgroundColor: 'hsl(var(--background))',
|
||||
fontSize: 14,
|
||||
border: '1px solid hsl(var(--border))',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
item: {
|
||||
padding: '5px 15px',
|
||||
borderBottom: '1px solid rgba(0,0,0,0.15)',
|
||||
border: 'none',
|
||||
'&focused': {
|
||||
backgroundColor: '#cee4e5',
|
||||
backgroundColor: 'hsl(var(--chart-2))',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export default {
|
||||
backgroundColor: '#cee4e5',
|
||||
backgroundColor: 'hsla(var(--chart-2) / 0.3)',
|
||||
position: 'relative',
|
||||
}
|
||||
@@ -23,11 +23,11 @@ export default function PageLayout({ children, className, title }: IProps) {
|
||||
: children;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col w-full h-full">
|
||||
{header}
|
||||
<main
|
||||
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,
|
||||
},
|
||||
|
||||
+65
-32
@@ -1,22 +1,39 @@
|
||||
import "yet-another-react-lightbox/styles.css";
|
||||
import FindInput from '@/components/find/FindInput';
|
||||
import PageLayout from '@/components/layouts/PageLayout';
|
||||
import Header from '@/components/shared/Header';
|
||||
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 { findAssets } from '@/handlers/api/asset.handler';
|
||||
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 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() {
|
||||
const { geminiEnabled } = useConfig();
|
||||
|
||||
const [index, setIndex] = useState(-1);
|
||||
const { geminiEnabled, exImmichUrl } = useConfig();
|
||||
const [query, setQuery] = useState('');
|
||||
const [assets, setAssets] = useState<IAsset[]>([]);
|
||||
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) => {
|
||||
setQuery(query);
|
||||
setLoading(true);
|
||||
@@ -42,6 +59,9 @@ export default function FindPage() {
|
||||
<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.
|
||||
</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>
|
||||
}
|
||||
else if (assets.length === 0) {
|
||||
@@ -50,23 +70,37 @@ export default function FindPage() {
|
||||
</div>
|
||||
}
|
||||
return (
|
||||
<div className="grid grid-cols-5 gap-4 p-2">
|
||||
{assets.map((asset) => (
|
||||
<div key={asset.id} className='w-full h-[200px] rounded-md overflow-hidden'>
|
||||
<Image
|
||||
src={ASSET_THUMBNAIL_PATH(asset.id)}
|
||||
alt={asset.id}
|
||||
style={{
|
||||
objectFit: 'cover',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
width={200}
|
||||
height={200}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<>
|
||||
<Lightbox
|
||||
slides={slides}
|
||||
open={index >= 0}
|
||||
index={index}
|
||||
close={() => setIndex(-1)}
|
||||
/>
|
||||
<div className="grid grid-cols-5 gap-4 p-2">
|
||||
{assets.map((asset, idx) => (
|
||||
<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
|
||||
src={ASSET_THUMBNAIL_PATH(asset.id)}
|
||||
alt={asset.id}
|
||||
style={{
|
||||
objectFit: 'cover',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
width={200}
|
||||
height={200}
|
||||
onClick={() => setIndex(idx)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -74,25 +108,24 @@ export default function FindPage() {
|
||||
<PageLayout>
|
||||
<Header leftComponent="Find" />
|
||||
{geminiEnabled ? (
|
||||
<div>
|
||||
<div className="flex flex-col gap-4 p-2">
|
||||
<FindInput onSearch={handleSearch} />
|
||||
</div>
|
||||
{renderContent()}
|
||||
</div>
|
||||
<>
|
||||
<div className="flex flex-col gap-4 p-2">
|
||||
<FindInput onSearch={handleSearch} />
|
||||
</div>
|
||||
{renderContent()}
|
||||
</>
|
||||
) : (
|
||||
<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' />
|
||||
<p className='text-lg font-semibold'>Google Gemini is not enabled</p>
|
||||
<p className='text-sm text-muted-foreground max-w-md text-center'>
|
||||
Currently, the Power Tools Find is relying on Google Gemini for parsing the query.
|
||||
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>
|
||||
<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'>
|
||||
<TriangleAlert className='w-4 h-4' />
|
||||
<span>Power tools uses Gemini only for parsing your query. None of your data is sent to Gemini.</span>
|
||||
</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>
|
||||
)}
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
--chart-3: 197 37% 24%;
|
||||
--chart-4: 43 74% 66%;
|
||||
--chart-5: 27 87% 67%;
|
||||
|
||||
}
|
||||
|
||||
.dark {
|
||||
@@ -88,3 +87,4 @@
|
||||
--chart-5: 340 75% 55%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user