Add progress feedback to add all photos button

This commit is contained in:
Sam Becker 2024-07-01 11:08:20 -05:00
parent 959b6f0bc2
commit 25b7629a60
7 changed files with 61 additions and 13 deletions

View File

@ -3,7 +3,6 @@
import ErrorNote from '@/components/ErrorNote';
import FieldSetWithStatus from '@/components/FieldSetWithStatus';
import Container from '@/components/Container';
import LoaderButton from '@/components/primitives/LoaderButton';
import { addAllUploadsAction } from '@/photo/actions';
import { PATH_ADMIN_PHOTOS } from '@/site/paths';
import {
@ -21,6 +20,7 @@ import { clsx } from 'clsx/lite';
import { useRouter } from 'next/navigation';
import { Dispatch, SetStateAction, useRef, useState } from 'react';
import { BiCheckCircle, BiImageAdd } from 'react-icons/bi';
import ProgressButton from '@/components/primitives/ProgressButton';
const UPLOAD_BATCH_SIZE = 4;
@ -45,6 +45,7 @@ export default function AdminAddAllUploads({
const [tags, setTags] = useState('');
const [actionErrorMessage, setActionErrorMessage] = useState('');
const [tagErrorMessage, setTagErrorMessage] = useState('');
const [addingProgress, setAddingProgress] = useState<number>();
const [isAddingComplete, setIsAddingComplete] = useState(false);
const router = useRouter();
@ -64,6 +65,10 @@ export default function AdminAddAllUploads({
: `Adding ${addedUploadUrls.current.length} of ${storageUrls.length}`
);
setButtonSubheadText(data?.subhead ?? '');
setAddingProgress((
addedUploadUrls.current.length /
storageUrls.length
) * 0.95);
setAddedUploadUrls?.(current => {
const urls = data?.addedUploadUrls.split(',') ?? [];
const updatedUrls = current
@ -76,6 +81,7 @@ export default function AdminAddAllUploads({
} catch (e: any) {
setIsAdding(false);
setButtonText('Try Again');
setAddingProgress(undefined);
setActionErrorMessage(e);
}
};
@ -131,8 +137,10 @@ export default function AdminAddAllUploads({
/>
</div>
<div className="space-y-2">
<LoaderButton
className="primary w-full justify-center"
<ProgressButton
primary
className="w-full justify-center"
progress={addingProgress}
isLoading={isAdding}
disabled={Boolean(tagErrorMessage) || isAddingComplete}
icon={isAddingComplete
@ -152,11 +160,13 @@ export default function AdminAddAllUploads({
}
setButtonText('Complete');
setButtonSubheadText('All uploads added');
setAddingProgress(1);
setIsAdding(false);
setIsAddingComplete(true);
await sleep(1000).then(() =>
router.push(PATH_ADMIN_PHOTOS));
} catch (e: any) {
setAddingProgress(undefined);
setIsAdding(false);
setButtonText('Try Again');
setActionErrorMessage(e);
@ -166,7 +176,7 @@ export default function AdminAddAllUploads({
hideTextOnMobile={false}
>
{buttonText}
</LoaderButton>
</ProgressButton>
{buttonSubheadText &&
<div className="text-dim text-sm text-center">
{buttonSubheadText}

View File

@ -42,9 +42,9 @@ export default function AdminOutdatedClient({
</span>
</>}
accessory={<LoaderButton
primary
icon={<IconGrSync className="translate-y-[1px]" />}
hideTextOnMobile={false}
className="primary"
onClick={async () => {
if (window.confirm(
// eslint-disable-next-line max-len

View File

@ -38,7 +38,7 @@ export default function AdminPhotosClient({
contentMain={
<div className="space-y-4">
<div className="flex">
<div className="grow">
<div className="grow min-w-0">
<PhotoUpload
shouldResize={!PRO_MODE_ENABLED}
isUploading={isUploading}

View File

@ -61,7 +61,6 @@ export default function ImageInput({
<LoaderButton
type="button"
isLoading={loading}
className="primary"
icon={<FiUploadCloud
size={18}
className="translate-x-[-0.5px] translate-y-[0.5px]"
@ -69,6 +68,7 @@ export default function ImageInput({
aria-disabled={loading}
onClick={() => inputRef.current?.click()}
hideTextOnMobile={false}
primary
>
{loading
? 'Uploading'

View File

@ -16,14 +16,12 @@ export default function SubmitButtonWithStatus({
children,
disabled,
className,
primary,
type: _type,
...buttonProps
}: {
onFormStatusChange?: (pending: boolean) => void
onFormSubmitToastMessage?: string
onFormSubmit?: () => void
primary?: boolean
} & ComponentProps<typeof LoaderButton>) {
const { pending } = useFormStatus();
@ -45,18 +43,17 @@ export default function SubmitButtonWithStatus({
return (
<LoaderButton
{...buttonProps}
type="submit"
disabled={disabled}
className={clsx(
className,
'inline-flex items-center gap-2',
primary && 'primary',
className,
)}
icon={icon}
spinnerColor={spinnerColor}
styleAs={styleAs}
isLoading={pending}
{...buttonProps}
>
{children}
</LoaderButton>

View File

@ -11,6 +11,7 @@ export default function LoaderButton(props: {
styleAs?: 'button' | 'link' | 'link-without-hover'
hideTextOnMobile?: boolean
shouldPreventDefault?: boolean
primary?: boolean
} & ButtonHTMLAttributes<HTMLButtonElement>) {
const {
children,
@ -20,6 +21,7 @@ export default function LoaderButton(props: {
styleAs = 'button',
hideTextOnMobile = true,
shouldPreventDefault,
primary,
type = 'button',
onClick,
disabled,
@ -41,10 +43,12 @@ export default function LoaderButton(props: {
'link h-4 active:text-medium',
'disabled:!bg-transparent',
]
: ['h-9']),
: ['h-9']
),
styleAs === 'link' && 'hover:text-dim',
styleAs === 'link-without-hover' && 'hover:text-main',
'inline-flex items-center gap-2 self-start whitespace-nowrap',
primary && 'primary',
className,
)}
disabled={isLoading || disabled}

View File

@ -0,0 +1,37 @@
'use client';
import { ComponentProps } from 'react';
import LoaderButton from './LoaderButton';
import { clsx } from 'clsx/lite';
export default function ProgressButton({
progress,
isLoading,
className,
children,
...props
}: {
progress?: number
} & ComponentProps<typeof LoaderButton>) {
return (
<LoaderButton
{...props}
isLoading={isLoading ?? ((progress ?? 1) < 1)}
className={clsx(
'relative overflow-hidden justify-center',
className,
)}
>
<div
style={{ transform: `scaleX(${progress ?? 0})`}}
className={clsx(
'absolute top-0 left-0 w-full',
'transition-all duration-500 origin-left',
'bg-invert h-[2px]',
progress === undefined ? 'opacity-0' : 'opacity-100',
)}
/>
{children}
</LoaderButton>
);
}