Improve upload state management
This commit is contained in:
parent
144e68b965
commit
f6bc865225
@ -119,7 +119,13 @@ export default function RootLayout({
|
||||
revalidatePath('/admin', 'layout');
|
||||
}}
|
||||
/>
|
||||
<AdminBatchEditPanel />
|
||||
<AdminBatchEditPanel
|
||||
onBatchActionComplete={async () => {
|
||||
'use server';
|
||||
// Update upload count in admin nav
|
||||
revalidatePath('/admin', 'layout');
|
||||
}}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@ -6,8 +6,8 @@ import {
|
||||
} from '@/utility/date';
|
||||
import { pathForAdminUploadUrl } from '@/app/paths';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { BiImageAdd } from 'react-icons/bi';
|
||||
import { ComponentProps, useState } from 'react';
|
||||
import IconAddUpload from '@/components/icons/IconAddUpload';
|
||||
|
||||
export default function AddUploadButton({
|
||||
url,
|
||||
@ -30,10 +30,7 @@ export default function AddUploadButton({
|
||||
return (
|
||||
<LoaderButton
|
||||
{...props}
|
||||
icon={<BiImageAdd
|
||||
size={18}
|
||||
className="translate-x-[1px] translate-y-[1px]"
|
||||
/>}
|
||||
icon={<IconAddUpload />}
|
||||
onClick={() => {
|
||||
onAddStart?.();
|
||||
setIsAddingLocal(true);
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import { getUniqueTagsCached } from '@/photo/cache';
|
||||
import AdminBatchEditPanelClient from './AdminBatchEditPanelClient';
|
||||
|
||||
export default async function AdminBatchEditPanel() {
|
||||
export default async function AdminBatchEditPanel({
|
||||
onBatchActionComplete,
|
||||
}: {
|
||||
onBatchActionComplete?: () => Promise<void>
|
||||
}) {
|
||||
const uniqueTags = await getUniqueTagsCached().catch(() => []);
|
||||
return (
|
||||
<AdminBatchEditPanelClient {...{ uniqueTags }} />
|
||||
<AdminBatchEditPanelClient {...{ uniqueTags, onBatchActionComplete }} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ import sleep from '@/utility/sleep';
|
||||
import { readStreamableValue } from 'ai/rsc';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Dispatch, SetStateAction, useRef, useState } from 'react';
|
||||
import { BiCheckCircle, BiImageAdd } from 'react-icons/bi';
|
||||
import { BiCheckCircle } from 'react-icons/bi';
|
||||
import ProgressButton from '@/components/primitives/ProgressButton';
|
||||
import { UrlAddStatus } from './AdminUploadsClient';
|
||||
import PhotoTagFieldset from './PhotoTagFieldset';
|
||||
@ -23,6 +23,7 @@ import { useAppState } from '@/state/AppState';
|
||||
import { pluralize } from '@/utility/string';
|
||||
import FieldsetFavs from '@/photo/form/FieldsetFavs';
|
||||
import FieldsetHidden from '@/photo/form/FieldsetHidden';
|
||||
import IconAddUpload from '@/components/icons/IconAddUpload';
|
||||
|
||||
const UPLOAD_BATCH_SIZE = 2;
|
||||
|
||||
@ -35,6 +36,7 @@ export default function AdminBatchUploadActions({
|
||||
setUrlAddStatuses,
|
||||
isDeleting,
|
||||
setIsDeleting,
|
||||
onBatchActionComplete,
|
||||
}: {
|
||||
uploadUrls: string[]
|
||||
uploadTitles: string[]
|
||||
@ -44,6 +46,7 @@ export default function AdminBatchUploadActions({
|
||||
setUrlAddStatuses: Dispatch<SetStateAction<UrlAddStatus[]>>
|
||||
isDeleting: boolean
|
||||
setIsDeleting: Dispatch<SetStateAction<boolean>>
|
||||
onBatchActionComplete?: () => Promise<void>
|
||||
}) {
|
||||
const { updateAdminData } = useAppState();
|
||||
|
||||
@ -177,10 +180,7 @@ export default function AdminBatchUploadActions({
|
||||
}
|
||||
icon={isAddingComplete
|
||||
? <BiCheckCircle size={18} className="translate-x-[1px]" />
|
||||
: <BiImageAdd
|
||||
size={18}
|
||||
className="translate-x-[1px] translate-y-[2px]"
|
||||
/>
|
||||
: <IconAddUpload />
|
||||
}
|
||||
onClick={async () => {
|
||||
// eslint-disable-next-line max-len
|
||||
@ -208,6 +208,7 @@ export default function AdminBatchUploadActions({
|
||||
setAddingProgress(1);
|
||||
setIsAdding(false);
|
||||
setIsAddingComplete(true);
|
||||
await onBatchActionComplete?.();
|
||||
await sleep(1000).then(() =>
|
||||
router.push(PATH_ADMIN_PHOTOS));
|
||||
} catch (e: any) {
|
||||
@ -225,9 +226,10 @@ export default function AdminBatchUploadActions({
|
||||
<DeleteUploadButton
|
||||
urls={uploadUrls}
|
||||
onDeleteStart={() => setIsDeleting(true)}
|
||||
onDelete={didFail => {
|
||||
onDelete={async didFail => {
|
||||
if (!didFail) {
|
||||
updateAdminData?.({ uploadsCount: 0 });
|
||||
await onBatchActionComplete?.();
|
||||
router.push(PATH_ADMIN_PHOTOS);
|
||||
} else {
|
||||
setIsDeleting(false);
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
import { StorageListItem, StorageListResponse } from '@/platforms/storage';
|
||||
import AdminBatchUploadActions from './AdminBatchUploadActions';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Tags } from '@/tag';
|
||||
import AdminUploadsTable from './AdminUploadsTable';
|
||||
|
||||
@ -20,14 +20,19 @@ export default function AdminUploadsClient({
|
||||
urls: StorageListResponse
|
||||
uniqueTags?: Tags
|
||||
}) {
|
||||
const [isAdding, setIsAdding] = useState(false);
|
||||
const [urlAddStatuses, setUrlAddStatuses] = useState<UrlAddStatus[]>(urls);
|
||||
|
||||
useEffect(() => {
|
||||
// Overwrite local state when server state changes
|
||||
setUrlAddStatuses(urls);
|
||||
}, [urls]);
|
||||
|
||||
const uploadUrls = useMemo(() => urlAddStatuses
|
||||
.map(({ url }) => url), [urlAddStatuses]);
|
||||
const uploadTitles = useMemo(() => urlAddStatuses
|
||||
.map(({ draftTitle }) => draftTitle ?? ''), [urlAddStatuses]);
|
||||
|
||||
const [isAdding, setIsAdding] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
|
||||
return (
|
||||
|
||||
@ -96,8 +96,8 @@ export default function AdminUploadsTableRow({
|
||||
'gap-2 sm:gap-3',
|
||||
'p-2 sm:p-3',
|
||||
)}>
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<div className="flex flex-col grow gap-3">
|
||||
<div className="flex flex-col gap-8 w-full">
|
||||
<div className="flex flex-col grow gap-2">
|
||||
<FieldSetWithStatus
|
||||
label="Title"
|
||||
value={draftTitle}
|
||||
@ -133,7 +133,7 @@ export default function AdminUploadsTableRow({
|
||||
<EditButton
|
||||
path={pathForAdminUploadUrl(url, draftTitle)}
|
||||
disabled={isRowLoading}
|
||||
tooltip="Review photo details"
|
||||
tooltip="Review EXIF details before adding"
|
||||
hideText="always"
|
||||
/>
|
||||
<DeleteUploadButton
|
||||
|
||||
15
src/components/icons/IconAddUpload.tsx
Normal file
15
src/components/icons/IconAddUpload.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { IconBaseProps } from 'react-icons';
|
||||
import { BiImageAdd } from 'react-icons/bi';
|
||||
|
||||
export default function IconAddUpload({
|
||||
className,
|
||||
size,
|
||||
...props
|
||||
}: IconBaseProps) {
|
||||
return <BiImageAdd
|
||||
{...props}
|
||||
size={size ?? 18}
|
||||
className={clsx('translate-x-[1px] translate-y-[1px]', className)}
|
||||
/>;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { uploadPhotoFromClient } from '@/platforms/storage';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { PATH_ADMIN_UPLOADS, pathForAdminUploadUrl } from '@/app/paths';
|
||||
import ImageInput from '../components/ImageInput';
|
||||
import { clsx } from 'clsx/lite';
|
||||
@ -49,6 +49,8 @@ export default function PhotoUploadWithStatus({
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const pathname = usePathname();
|
||||
|
||||
useEffect(() => {
|
||||
// Hide upload panel while button is shown
|
||||
if (showButton) {
|
||||
@ -127,9 +129,14 @@ export default function PhotoUploadWithStatus({
|
||||
if (isLastBlob) {
|
||||
await onLastUpload?.();
|
||||
shouldResetUploadStateAfterPending.current = true;
|
||||
startTransition(() => hasMultipleUploads
|
||||
? router.push(PATH_ADMIN_UPLOADS)
|
||||
: router.push(pathForAdminUploadUrl(url)));
|
||||
if (pathname === PATH_ADMIN_UPLOADS) {
|
||||
setUploadState?.({ isUploading: false });
|
||||
router.refresh();
|
||||
} else {
|
||||
startTransition(() => hasMultipleUploads
|
||||
? router.push(PATH_ADMIN_UPLOADS)
|
||||
: router.push(pathForAdminUploadUrl(url)));
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
||||
@ -47,6 +47,7 @@ import PhotoFilmIcon from '@/film/PhotoFilmIcon';
|
||||
import FieldsetFavs from './FieldsetFavs';
|
||||
import FieldsetHidden from './FieldsetHidden';
|
||||
import { useAppText } from '@/i18n/state/client';
|
||||
import IconAddUpload from '@/components/icons/IconAddUpload';
|
||||
|
||||
const THUMBNAIL_SIZE = 300;
|
||||
|
||||
@ -469,12 +470,13 @@ export default function PhotoForm({
|
||||
Cancel
|
||||
</Link>
|
||||
<SubmitButtonWithStatus
|
||||
icon={type === 'create' && <IconAddUpload />}
|
||||
disabled={!canFormBeSubmitted}
|
||||
onFormStatusChange={onFormStatusChange}
|
||||
onFormSubmit={invalidateSwr}
|
||||
primary
|
||||
>
|
||||
{type === 'create' ? 'Create' : 'Update'}
|
||||
{type === 'create' ? 'Add' : 'Update'}
|
||||
</SubmitButtonWithStatus>
|
||||
<div className={clsx(
|
||||
'absolute -top-16 -left-2 right-0 bottom-0 -z-10',
|
||||
|
||||
@ -58,7 +58,7 @@ export type AppStateContextType = {
|
||||
setIsPerformingSelectEdit?: Dispatch<SetStateAction<boolean>>
|
||||
insightsIndicatorStatus?: InsightsIndicatorStatus
|
||||
// UPLOAD
|
||||
startUpload?: (onStart?: () => void) => void
|
||||
startUpload?: () => Promise<boolean>
|
||||
uploadInputRef?: RefObject<HTMLInputElement | null>
|
||||
uploadState: UploadState
|
||||
setUploadState?: (uploadState: Partial<UploadState>) => void
|
||||
|
||||
Loading…
Reference in New Issue
Block a user