Refactor core photo chooser behavior
This commit is contained in:
parent
7182e6db0e
commit
38f724762e
@ -1,8 +1,9 @@
|
|||||||
import AdminComponentPageClient from '@/admin/AdminComponentPageClient';
|
import AdminComponentPageClient from '@/admin/AdminComponentPageClient';
|
||||||
|
import { INFINITE_SCROLL_GRID_INITIAL } from '@/photo';
|
||||||
import { getPhotosCached, getPhotosMetaCached } from '@/photo/cache';
|
import { getPhotosCached, getPhotosMetaCached } from '@/photo/cache';
|
||||||
|
|
||||||
export default async function ComponentsPage() {
|
export default async function ComponentsPage() {
|
||||||
const photos = await getPhotosCached({ limit: 50 });
|
const photos = await getPhotosCached({ limit: INFINITE_SCROLL_GRID_INITIAL });
|
||||||
const photosCount = await getPhotosMetaCached()
|
const photosCount = await getPhotosMetaCached()
|
||||||
.then(({ count }) => count);
|
.then(({ count }) => count);
|
||||||
|
|
||||||
|
|||||||
@ -22,14 +22,12 @@ export default function AdminAboutEditPage({
|
|||||||
photoHero: _photoHero,
|
photoHero: _photoHero,
|
||||||
photos,
|
photos,
|
||||||
photosCount,
|
photosCount,
|
||||||
photosHidden,
|
|
||||||
}: {
|
}: {
|
||||||
about?: About
|
about?: About
|
||||||
photoAvatar?: Photo
|
photoAvatar?: Photo
|
||||||
photoHero?: Photo
|
photoHero?: Photo
|
||||||
photos?: Photo[]
|
photos: Photo[]
|
||||||
photosCount?: number
|
photosCount: number
|
||||||
photosHidden?: Photo[]
|
|
||||||
shouldResizeImages?: boolean
|
shouldResizeImages?: boolean
|
||||||
}) {
|
}) {
|
||||||
const appText = useAppText();
|
const appText = useAppText();
|
||||||
@ -73,7 +71,6 @@ export default function AdminAboutEditPage({
|
|||||||
photo={photoAvatar}
|
photo={photoAvatar}
|
||||||
photos={photos}
|
photos={photos}
|
||||||
photosCount={photosCount}
|
photosCount={photosCount}
|
||||||
photosHidden={photosHidden}
|
|
||||||
/>
|
/>
|
||||||
<PhotoAvatar photo={photoAvatar} />
|
<PhotoAvatar photo={photoAvatar} />
|
||||||
<FieldsetWithStatus
|
<FieldsetWithStatus
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import { Photo } from '@/photo';
|
|||||||
import FieldsetPhotoQuery from '@/photo/form/FieldsetPhotoQuery';
|
import FieldsetPhotoQuery from '@/photo/form/FieldsetPhotoQuery';
|
||||||
import FieldsetPhotoChooser from '@/photo/form/FieldsetPhotoChooser';
|
import FieldsetPhotoChooser from '@/photo/form/FieldsetPhotoChooser';
|
||||||
|
|
||||||
export default function ComponentsPageClient({
|
export default function AdminComponentPageClient({
|
||||||
photo,
|
photo,
|
||||||
photos,
|
photos,
|
||||||
photosCount,
|
photosCount,
|
||||||
|
|||||||
@ -250,7 +250,7 @@ export default function CommandKClient({
|
|||||||
photos,
|
photos,
|
||||||
isLoading,
|
isLoading,
|
||||||
reset,
|
reset,
|
||||||
} = usePhotoQuery(query, !isPending);
|
} = usePhotoQuery({ query, isEnabled: !isPending });
|
||||||
|
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import clsx from 'clsx/lite';
|
import clsx from 'clsx/lite';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
import Spinner from './Spinner';
|
||||||
|
|
||||||
export default function SegmentMenu<T extends string>({
|
export default function SegmentMenu<T extends string>({
|
||||||
items,
|
items,
|
||||||
@ -11,6 +12,7 @@ export default function SegmentMenu<T extends string>({
|
|||||||
value: T
|
value: T
|
||||||
icon?: ReactNode
|
icon?: ReactNode
|
||||||
iconSelected?: ReactNode
|
iconSelected?: ReactNode
|
||||||
|
isLoading?: boolean
|
||||||
}[]
|
}[]
|
||||||
selected: T
|
selected: T
|
||||||
onChange: (value: T) => void
|
onChange: (value: T) => void
|
||||||
@ -21,7 +23,7 @@ export default function SegmentMenu<T extends string>({
|
|||||||
'flex justify-center gap-1',
|
'flex justify-center gap-1',
|
||||||
className,
|
className,
|
||||||
)}>
|
)}>
|
||||||
{items.map(({ value, icon, iconSelected }) => (
|
{items.map(({ value, icon, iconSelected, isLoading }) => (
|
||||||
<button
|
<button
|
||||||
key={value}
|
key={value}
|
||||||
onClick={() => onChange(value)}
|
onClick={() => onChange(value)}
|
||||||
@ -36,15 +38,17 @@ export default function SegmentMenu<T extends string>({
|
|||||||
'active:bg-extra-dim',
|
'active:bg-extra-dim',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{icon
|
{isLoading
|
||||||
? selected === value && iconSelected
|
? <Spinner />
|
||||||
? iconSelected
|
: icon
|
||||||
: icon
|
? selected === value && iconSelected
|
||||||
: <span className={clsx(
|
? iconSelected
|
||||||
'text-sm font-medium uppercase tracking-wider',
|
: icon
|
||||||
)}>
|
: <span className={clsx(
|
||||||
{value}
|
'text-sm font-medium uppercase tracking-wider',
|
||||||
</span>}
|
)}>
|
||||||
|
{value}
|
||||||
|
</span>}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -38,6 +38,7 @@ export default function InfinitePhotoScroll({
|
|||||||
recipe,
|
recipe,
|
||||||
film,
|
film,
|
||||||
focal,
|
focal,
|
||||||
|
moreButtonClassName = 'mt-4',
|
||||||
wrapMoreButtonInGrid,
|
wrapMoreButtonInGrid,
|
||||||
useCachedPhotos = true,
|
useCachedPhotos = true,
|
||||||
includeHiddenPhotos,
|
includeHiddenPhotos,
|
||||||
@ -49,6 +50,7 @@ export default function InfinitePhotoScroll({
|
|||||||
sortWithPriority?: boolean
|
sortWithPriority?: boolean
|
||||||
excludeFromFeeds?: boolean
|
excludeFromFeeds?: boolean
|
||||||
cacheKey: string
|
cacheKey: string
|
||||||
|
moreButtonClassName?: string
|
||||||
wrapMoreButtonInGrid?: boolean
|
wrapMoreButtonInGrid?: boolean
|
||||||
useCachedPhotos?: boolean
|
useCachedPhotos?: boolean
|
||||||
includeHiddenPhotos?: boolean
|
includeHiddenPhotos?: boolean
|
||||||
@ -178,7 +180,7 @@ export default function InfinitePhotoScroll({
|
|||||||
revalidatePhoto,
|
revalidatePhoto,
|
||||||
})
|
})
|
||||||
))}
|
))}
|
||||||
{!isFinished && <div className="mt-4">
|
{!isFinished && <div className={moreButtonClassName}>
|
||||||
{wrapMoreButtonInGrid
|
{wrapMoreButtonInGrid
|
||||||
? <AppGrid contentMain={renderMoreButton} />
|
? <AppGrid contentMain={renderMoreButton} />
|
||||||
: renderMoreButton}
|
: renderMoreButton}
|
||||||
|
|||||||
@ -798,7 +798,7 @@ export const getPhotosCachedAction = async (
|
|||||||
|
|
||||||
// Public actions
|
// Public actions
|
||||||
|
|
||||||
export const searchPhotosAction = async (query: string) =>
|
export const searchPhotosPublicAction = async (query: string) =>
|
||||||
getPhotos({ query, limit: 10 })
|
getPhotos({ query, limit: 10 })
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error('Could not query photos', e);
|
console.error('Could not query photos', e);
|
||||||
|
|||||||
@ -1,55 +1,55 @@
|
|||||||
|
/* eslint-disable react-hooks/set-state-in-effect */
|
||||||
import FieldsetWithStatus from '@/components/FieldsetWithStatus';
|
import FieldsetWithStatus from '@/components/FieldsetWithStatus';
|
||||||
import { altTextForPhoto, doesPhotoNeedBlurCompatibility, Photo } from '..';
|
import {
|
||||||
|
altTextForPhoto,
|
||||||
|
doesPhotoNeedBlurCompatibility,
|
||||||
|
INFINITE_SCROLL_GRID_MULTIPLE,
|
||||||
|
Photo,
|
||||||
|
} from '..';
|
||||||
import clsx from 'clsx/lite';
|
import clsx from 'clsx/lite';
|
||||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||||
import ImageMedium from '@/components/image/ImageMedium';
|
import ImageMedium from '@/components/image/ImageMedium';
|
||||||
import { menuSurfaceStyles } from '@/components/primitives/surface';
|
import { menuSurfaceStyles } from '@/components/primitives/surface';
|
||||||
import { GRID_SPACE_CLASSNAME } from '@/components';
|
|
||||||
import useDynamicPhoto from '../useDynamicPhoto';
|
|
||||||
import { IoSearch } from 'react-icons/io5';
|
import { IoSearch } from 'react-icons/io5';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import usePhotoQuery from '../usePhotoQuery';
|
import usePhotoQuery from '../usePhotoQuery';
|
||||||
import Spinner from '@/components/Spinner';
|
|
||||||
import { BiChevronDown } from 'react-icons/bi';
|
import { BiChevronDown } from 'react-icons/bi';
|
||||||
import SegmentMenu from '@/components/SegmentMenu';
|
import SegmentMenu from '@/components/SegmentMenu';
|
||||||
import IconFavs from '@/components/icons/IconFavs';
|
import IconFavs from '@/components/icons/IconFavs';
|
||||||
import IconLock from '@/components/icons/IconLock';
|
import InfinitePhotoScroll from '../InfinitePhotoScroll';
|
||||||
|
|
||||||
type Mode = 'all' | 'favs' | 'hidden' | 'search';
|
type Mode = 'all' | 'favs' | 'search';
|
||||||
|
|
||||||
const renderPhoto = ({
|
const CLASSNAME_GRID = 'grid grid-cols-3 gap-0.5';
|
||||||
photo,
|
|
||||||
className,
|
const renderPhoto = (photo: Photo) =>
|
||||||
onClick,
|
|
||||||
}: {
|
|
||||||
photo: Photo,
|
|
||||||
className?: string,
|
|
||||||
onClick?: () => void,
|
|
||||||
}) =>
|
|
||||||
<ImageMedium
|
<ImageMedium
|
||||||
src={photo.url}
|
src={photo.url}
|
||||||
alt={altTextForPhoto(photo)}
|
alt={altTextForPhoto(photo)}
|
||||||
aspectRatio={photo.aspectRatio}
|
aspectRatio={photo.aspectRatio}
|
||||||
blurDataURL={photo.blurData}
|
blurDataURL={photo.blurData}
|
||||||
blurCompatibilityMode={doesPhotoNeedBlurCompatibility(photo)}
|
blurCompatibilityMode={doesPhotoNeedBlurCompatibility(photo)}
|
||||||
{...{ className, onClick }}
|
|
||||||
/>;
|
/>;
|
||||||
|
|
||||||
export default function FieldsetPhotoChooser({
|
export default function FieldsetPhotoChooser({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
photo,
|
photo: _photo,
|
||||||
photos = [],
|
photos = [],
|
||||||
|
photosCount,
|
||||||
}: {
|
}: {
|
||||||
label: string
|
label: string
|
||||||
value: string
|
value: string
|
||||||
onChange: (value: string) => void
|
onChange: (photoId: string) => void
|
||||||
photo?: Photo
|
photo?: Photo
|
||||||
photos?: Photo[]
|
photos: Photo[]
|
||||||
photosCount?: number
|
photosCount: number
|
||||||
photosHidden?: Photo[]
|
|
||||||
}) {
|
}) {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
|
const [photo, setPhoto] = useState(_photo);
|
||||||
|
|
||||||
const [mode, setMode] = useState<Mode>('all');
|
const [mode, setMode] = useState<Mode>('all');
|
||||||
|
|
||||||
const showQuery = mode === 'search';
|
const showQuery = mode === 'search';
|
||||||
@ -59,42 +59,60 @@ export default function FieldsetPhotoChooser({
|
|||||||
const [query, setQuery] = useState('');
|
const [query, setQuery] = useState('');
|
||||||
const {
|
const {
|
||||||
photos: photosQuery,
|
photos: photosQuery,
|
||||||
isLoading,
|
isLoading: isLoadingPhotoQuery,
|
||||||
reset,
|
reset: resetPhotoQuery,
|
||||||
} = usePhotoQuery(query);
|
} = usePhotoQuery({ query, isPrivate: true });
|
||||||
|
|
||||||
const {
|
const reset = useCallback((resetMenu?: boolean) => {
|
||||||
photo: photoAvatar,
|
resetPhotoQuery();
|
||||||
isLoading: isLoadingPhotoAvatar,
|
setQuery('');
|
||||||
} = useDynamicPhoto({
|
if (resetMenu) { setMode('all'); }
|
||||||
initialPhoto: photo,
|
}, [resetPhotoQuery]);
|
||||||
photoId: value,
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Focus input on query mode
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (showQuery) {
|
if (showQuery) { inputRef.current?.focus(); }
|
||||||
inputRef.current?.focus();
|
}, [showQuery]);
|
||||||
} else {
|
|
||||||
reset();
|
// Reset menu when closed
|
||||||
// eslint-disable-next-line react-hooks/set-state-in-effect
|
useEffect(() => {
|
||||||
setQuery('');
|
if (!isOpen) { reset(true); }
|
||||||
}
|
}, [isOpen, reset]);
|
||||||
}, [showQuery, reset]);
|
|
||||||
|
const renderPhotoButton = (photo: Photo) =>
|
||||||
|
<span
|
||||||
|
key={photo.id}
|
||||||
|
className={clsx(
|
||||||
|
'flex w-full aspect-square object-cover',
|
||||||
|
'overflow-hidden select-none active:opacity-75',
|
||||||
|
'cursor-pointer',
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
setPhoto(photo);
|
||||||
|
onChange(photo.id);
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{renderPhoto(photo)}
|
||||||
|
</span>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<FieldsetWithStatus {...{ label, value, onChange, type: 'hidden' }} />
|
<FieldsetWithStatus {...{ label, value, onChange, type: 'hidden' }} />
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DropdownMenu.Trigger asChild>
|
<DropdownMenu.Trigger asChild>
|
||||||
<button type="button" className="inline-flex flex-col p-1.5">
|
<button type="button" className={clsx(
|
||||||
|
'inline-flex flex-col p-1.5 pt-0 gap-0',
|
||||||
|
)}>
|
||||||
<span className={clsx(
|
<span className={clsx(
|
||||||
'w-full',
|
'w-full',
|
||||||
'flex items-center gap-1',
|
'flex items-center gap-1',
|
||||||
'font-sans',
|
'font-sans',
|
||||||
'text-xs text-medium font-medium uppercase tracking-wider',
|
'text-xs text-medium font-medium uppercase tracking-wider',
|
||||||
|
'py-1',
|
||||||
)}>
|
)}>
|
||||||
<span className="grow truncate text-left">
|
<span className="grow truncate text-left">
|
||||||
Avatar
|
{label}
|
||||||
</span>
|
</span>
|
||||||
<BiChevronDown size={18} />
|
<BiChevronDown size={18} />
|
||||||
</span>
|
</span>
|
||||||
@ -103,10 +121,7 @@ export default function FieldsetPhotoChooser({
|
|||||||
'border border-medium rounded-[4px]',
|
'border border-medium rounded-[4px]',
|
||||||
'overflow-hidden select-none active:opacity-75',
|
'overflow-hidden select-none active:opacity-75',
|
||||||
)}>
|
)}>
|
||||||
{photoAvatar && renderPhoto({
|
{photo && renderPhoto(photo)}
|
||||||
photo: photoAvatar,
|
|
||||||
className: clsx(isLoadingPhotoAvatar && 'opacity-50'),
|
|
||||||
})}
|
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
@ -115,59 +130,65 @@ export default function FieldsetPhotoChooser({
|
|||||||
onCloseAutoFocus={e => e.preventDefault()}
|
onCloseAutoFocus={e => e.preventDefault()}
|
||||||
align="start"
|
align="start"
|
||||||
sideOffset={10}
|
sideOffset={10}
|
||||||
className={menuSurfaceStyles('z-20 px-1.5 py-1.5 rounded-2xl')}
|
className={menuSurfaceStyles('z-20 rounded-2xl pb-0 overflow-auto')}
|
||||||
>
|
>
|
||||||
|
<SegmentMenu
|
||||||
|
className="pt-1 pb-2 px-1.5"
|
||||||
|
items={[{
|
||||||
|
value: 'all',
|
||||||
|
}, {
|
||||||
|
value: 'favs',
|
||||||
|
icon: <IconFavs size={16} />,
|
||||||
|
iconSelected: <IconFavs size={16} highlight />,
|
||||||
|
}, {
|
||||||
|
value: 'search',
|
||||||
|
icon: <IoSearch size={16} />,
|
||||||
|
isLoading: isLoadingPhotoQuery,
|
||||||
|
}]}
|
||||||
|
selected={mode}
|
||||||
|
onChange={mode => {
|
||||||
|
setMode(mode);
|
||||||
|
if (mode !== 'search') {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
GRID_SPACE_CLASSNAME,
|
'border-t border-medium',
|
||||||
'w-[18rem] max-h-[20rem] rounded-xl overflow-y-auto',
|
'p-1',
|
||||||
|
!showQuery && 'hidden',
|
||||||
)}>
|
)}>
|
||||||
<SegmentMenu
|
<input
|
||||||
className="pt-1 pb-2 px-1.5"
|
id="query"
|
||||||
items={[{
|
ref={inputRef}
|
||||||
value: 'all',
|
type="text"
|
||||||
}, {
|
placeholder="Search for a photo"
|
||||||
value: 'favs',
|
className="block w-full m-0 border-none outline-none"
|
||||||
icon: <IconFavs size={16} />,
|
value={query}
|
||||||
iconSelected: <IconFavs size={16} highlight />,
|
onChange={e => setQuery(e.target.value)}
|
||||||
}, {
|
|
||||||
value: 'hidden',
|
|
||||||
icon: <IconLock size={15} />,
|
|
||||||
}, {
|
|
||||||
value: 'search',
|
|
||||||
icon: isLoading
|
|
||||||
? <Spinner />
|
|
||||||
: <IoSearch size={16} />,
|
|
||||||
}]}
|
|
||||||
selected={mode}
|
|
||||||
onChange={setMode}
|
|
||||||
/>
|
/>
|
||||||
{showQuery &&
|
</div>
|
||||||
<input
|
<div className={clsx(
|
||||||
ref={inputRef}
|
'w-[18rem] max-h-[20rem] overflow-y-auto',
|
||||||
type="text"
|
'space-y-0.5',
|
||||||
placeholder="Search for a photo"
|
)}>
|
||||||
className="block w-full m-0"
|
<div className={CLASSNAME_GRID}>
|
||||||
value={query}
|
{(showQuery && query ? photosQuery : photos)
|
||||||
onChange={e => setQuery(e.target.value)}
|
.map(photo => renderPhotoButton(photo))}
|
||||||
/>}
|
|
||||||
<div className={clsx(
|
|
||||||
'grid grid-cols-3 gap-0.5',
|
|
||||||
)}>
|
|
||||||
{(showQuery && query ? photosQuery : photos).map(photo => (
|
|
||||||
<span
|
|
||||||
key={photo.id}
|
|
||||||
className={clsx(
|
|
||||||
'flex w-full aspect-square object-cover',
|
|
||||||
'overflow-hidden select-none active:opacity-75',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{renderPhoto({
|
|
||||||
photo,
|
|
||||||
onClick: () => onChange(photo.id),
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
{!(showQuery && query) && photosCount > photos.length &&
|
||||||
|
<InfinitePhotoScroll
|
||||||
|
cacheKey="photo-chooser"
|
||||||
|
initialOffset={photos.length}
|
||||||
|
itemsPerPage={INFINITE_SCROLL_GRID_MULTIPLE}
|
||||||
|
moreButtonClassName="mt-2"
|
||||||
|
>
|
||||||
|
{({ key, photos }) => (
|
||||||
|
<div key={key} className={CLASSNAME_GRID}>
|
||||||
|
{photos.map(photo => renderPhotoButton(photo))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</InfinitePhotoScroll>}
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Portal>
|
</DropdownMenu.Portal>
|
||||||
|
|||||||
@ -2,16 +2,22 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { Photo } from '.';
|
import { Photo } from '.';
|
||||||
import { useDebounce } from 'use-debounce';
|
import { useDebounce } from 'use-debounce';
|
||||||
import { searchPhotosAction } from './actions';
|
import { getPhotosAction, searchPhotosPublicAction } from './actions';
|
||||||
|
|
||||||
const formatQuery = (query: string) =>
|
const formatQuery = (query: string) =>
|
||||||
query.trim().toLocaleLowerCase();
|
query.trim().toLocaleLowerCase();
|
||||||
|
|
||||||
export default function usePhotoQuery(
|
export default function usePhotoQuery({
|
||||||
query: string,
|
query,
|
||||||
isEnabled = true,
|
isEnabled = true,
|
||||||
minimumQueryLength = 2,
|
minimumQueryLength = 2,
|
||||||
) {
|
isPrivate,
|
||||||
|
}: {
|
||||||
|
query: string
|
||||||
|
isEnabled?: boolean
|
||||||
|
minimumQueryLength?: number
|
||||||
|
isPrivate?: boolean
|
||||||
|
}) {
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
const queryFormatted = useMemo(() =>
|
const queryFormatted = useMemo(() =>
|
||||||
@ -30,7 +36,9 @@ export default function usePhotoQuery(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (queryDebounced.length >= minimumQueryLength && isEnabled) {
|
if (queryDebounced.length >= minimumQueryLength && isEnabled) {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
searchPhotosAction(queryDebounced)
|
(isPrivate
|
||||||
|
? getPhotosAction({ query: queryDebounced })
|
||||||
|
: searchPhotosPublicAction(queryDebounced))
|
||||||
.then(setPhotos)
|
.then(setPhotos)
|
||||||
.finally(() => setIsLoading(false));
|
.finally(() => setIsLoading(false));
|
||||||
}
|
}
|
||||||
@ -38,6 +46,7 @@ export default function usePhotoQuery(
|
|||||||
queryDebounced,
|
queryDebounced,
|
||||||
minimumQueryLength,
|
minimumQueryLength,
|
||||||
isEnabled,
|
isEnabled,
|
||||||
|
isPrivate,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user