Batch upload adding action
This commit is contained in:
parent
68c92796ae
commit
6326db0a18
@ -15,24 +15,27 @@ import {
|
||||
generateLocalNaivePostgresString,
|
||||
generateLocalPostgresString,
|
||||
} from '@/utility/date';
|
||||
import sleep from '@/utility/sleep';
|
||||
import { readStreamableValue } from 'ai/rsc';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useRef, useState } from 'react';
|
||||
import { BiImageAdd } from 'react-icons/bi';
|
||||
import { Dispatch, SetStateAction, useRef, useState } from 'react';
|
||||
import { BiCheckCircle, BiImageAdd } from 'react-icons/bi';
|
||||
|
||||
const UPLOAD_BATCH_SIZE = 4;
|
||||
|
||||
export default function AdminAddAllUploads({
|
||||
storageUrlCount,
|
||||
storageUrls,
|
||||
uniqueTags,
|
||||
isAdding,
|
||||
setIsAdding,
|
||||
onUploadAdded,
|
||||
setAddedUploadUrls,
|
||||
}: {
|
||||
storageUrlCount: number
|
||||
storageUrls: string[]
|
||||
uniqueTags?: TagsWithMeta
|
||||
isAdding: boolean
|
||||
setIsAdding: (isAdding: boolean) => void
|
||||
onUploadAdded?: (addedUploadUrls: string[]) => void
|
||||
setAddedUploadUrls?: Dispatch<SetStateAction<string[]>>
|
||||
}) {
|
||||
const divRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
@ -42,9 +45,41 @@ export default function AdminAddAllUploads({
|
||||
const [tags, setTags] = useState('');
|
||||
const [actionErrorMessage, setActionErrorMessage] = useState('');
|
||||
const [tagErrorMessage, setTagErrorMessage] = useState('');
|
||||
const [isAddingComplete, setIsAddingComplete] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const addedUploadUrls = useRef<string[]>([]);
|
||||
const addUploadUrls = async (uploadUrls: string[]) => {
|
||||
try {
|
||||
const stream = await addAllUploadsAction({
|
||||
uploadUrls,
|
||||
tags: showTags ? tags : undefined,
|
||||
takenAtLocal: generateLocalPostgresString(),
|
||||
takenAtNaiveLocal: generateLocalNaivePostgresString(),
|
||||
});
|
||||
for await (const data of readStreamableValue(stream)) {
|
||||
setButtonText(addedUploadUrls.current.length === 0
|
||||
? `Adding ${storageUrls.length} uploads`
|
||||
: `Adding ${addedUploadUrls.current.length} of ${storageUrls.length}`
|
||||
);
|
||||
setButtonSubheadText(data?.subhead ?? '');
|
||||
setAddedUploadUrls?.(current => {
|
||||
const urls = data?.addedUploadUrls.split(',') ?? [];
|
||||
const updatedUrls = current
|
||||
.filter(url => !urls.includes(url))
|
||||
.concat(urls);
|
||||
addedUploadUrls.current = updatedUrls;
|
||||
return updatedUrls;
|
||||
});
|
||||
}
|
||||
} catch (e: any) {
|
||||
setIsAdding(false);
|
||||
setButtonText('Try Again');
|
||||
setActionErrorMessage(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{actionErrorMessage &&
|
||||
@ -58,7 +93,7 @@ export default function AdminAddAllUploads({
|
||||
)}>
|
||||
{showTags
|
||||
? tagErrorMessage || 'Add tags to all uploads'
|
||||
: `Found ${storageUrlCount} uploads`}
|
||||
: `Found ${storageUrls.length} uploads`}
|
||||
</div>
|
||||
<FieldSetWithStatus
|
||||
id="show-tags"
|
||||
@ -99,25 +134,28 @@ export default function AdminAddAllUploads({
|
||||
<LoaderButton
|
||||
className="primary w-full justify-center"
|
||||
isLoading={isAdding}
|
||||
disabled={Boolean(tagErrorMessage)}
|
||||
icon={<BiImageAdd size={18} className="translate-x-[1px]" />}
|
||||
disabled={Boolean(tagErrorMessage) || isAddingComplete}
|
||||
icon={isAddingComplete
|
||||
? <BiCheckCircle size={18} className="translate-x-[1px]" />
|
||||
: <BiImageAdd size={18} className="translate-x-[1px]" />
|
||||
}
|
||||
onClick={async () => {
|
||||
if (confirm(
|
||||
`Are you sure you want to add all ${storageUrlCount} uploads?`
|
||||
)) {
|
||||
// eslint-disable-next-line max-len
|
||||
if (confirm(`Are you sure you want to add all ${storageUrls.length} uploads?`)) {
|
||||
setIsAdding(true);
|
||||
let uploadsToAdd = storageUrls.slice();
|
||||
try {
|
||||
const stream = await addAllUploadsAction({
|
||||
tags: showTags ? tags : undefined,
|
||||
takenAtLocal: generateLocalPostgresString(),
|
||||
takenAtNaiveLocal: generateLocalNaivePostgresString(),
|
||||
});
|
||||
for await (const data of readStreamableValue(stream)) {
|
||||
setButtonText(data?.headline ?? '');
|
||||
setButtonSubheadText(data?.subhead ?? '');
|
||||
onUploadAdded?.(data?.addedUploadUrls.split(',') ?? []);
|
||||
while (uploadsToAdd.length > 0) {
|
||||
await addUploadUrls(
|
||||
uploadsToAdd.splice(0, UPLOAD_BATCH_SIZE),
|
||||
);
|
||||
}
|
||||
router.push(PATH_ADMIN_PHOTOS);
|
||||
setButtonText('Complete');
|
||||
setButtonSubheadText('All uploads added');
|
||||
setIsAdding(false);
|
||||
setIsAddingComplete(true);
|
||||
await sleep(1000).then(() =>
|
||||
router.push(PATH_ADMIN_PHOTOS));
|
||||
} catch (e: any) {
|
||||
setIsAdding(false);
|
||||
setButtonText('Try Again');
|
||||
|
||||
@ -21,11 +21,11 @@ export default function AdminUploadsClient({
|
||||
<div className="space-y-4">
|
||||
{urls.length > 1 &&
|
||||
<AdminAddAllUploads
|
||||
storageUrlCount={urls.length}
|
||||
storageUrls={urls.map(({ url }) => url)}
|
||||
uniqueTags={uniqueTags}
|
||||
isAdding={isAdding}
|
||||
setIsAdding={setIsAdding}
|
||||
onUploadAdded={setAddedUploadUrls}
|
||||
setAddedUploadUrls={setAddedUploadUrls}
|
||||
/>}
|
||||
<AdminUploadsTable {...{ title, urls, isAdding, addedUploadUrls }} />
|
||||
</div>
|
||||
|
||||
@ -43,7 +43,6 @@ import {
|
||||
AI_TEXT_GENERATION_ENABLED,
|
||||
BLUR_ENABLED,
|
||||
} from '@/site/config';
|
||||
import { getStorageUploadUrlsNoStore } from '@/services/storage/cache';
|
||||
import { generateAiImageQueries } from './ai/server';
|
||||
import { createStreamableValue } from 'ai/rsc';
|
||||
import { convertUploadToPhoto } from './storage';
|
||||
@ -71,38 +70,29 @@ export const createPhotoAction = async (formData: FormData) =>
|
||||
});
|
||||
|
||||
export const addAllUploadsAction = async ({
|
||||
uploadUrls,
|
||||
tags,
|
||||
takenAtLocal,
|
||||
takenAtNaiveLocal,
|
||||
limit,
|
||||
}: {
|
||||
uploadUrls: string[]
|
||||
tags?: string
|
||||
takenAtLocal: string
|
||||
takenAtNaiveLocal: string
|
||||
limit?: number
|
||||
}) =>
|
||||
runAuthenticatedAdminServerAction(async () => {
|
||||
const uploadUrls = (await getStorageUploadUrlsNoStore())
|
||||
.slice(0, limit);
|
||||
const uploadTotal = uploadUrls.length;
|
||||
const addedUploadUrls: string[] = [];
|
||||
|
||||
const stream = createStreamableValue<{
|
||||
headline: string,
|
||||
subhead?: string,
|
||||
addedUploadUrls: string,
|
||||
}, string>({
|
||||
headline: `Adding ${uploadTotal} Photos...`,
|
||||
const stream = createStreamableValue({
|
||||
subhead: '',
|
||||
addedUploadUrls: '',
|
||||
});
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
for (const [index, { url }] of uploadUrls.entries()) {
|
||||
const headline = `Adding ${index + 1} of ${uploadTotal}`;
|
||||
|
||||
for (const url of uploadUrls) {
|
||||
stream.update({
|
||||
headline,
|
||||
subhead: 'Parsing EXIF data',
|
||||
addedUploadUrls: addedUploadUrls.join(','),
|
||||
});
|
||||
@ -121,7 +111,6 @@ export const addAllUploadsAction = async ({
|
||||
if (photoFormExif) {
|
||||
if (AI_TEXT_GENERATION_ENABLED) {
|
||||
stream.update({
|
||||
headline,
|
||||
subhead: 'Generating AI text',
|
||||
addedUploadUrls: addedUploadUrls.join(','),
|
||||
});
|
||||
@ -148,7 +137,6 @@ export const addAllUploadsAction = async ({
|
||||
};
|
||||
|
||||
stream.update({
|
||||
headline,
|
||||
subhead: 'Moving upload to photo storage',
|
||||
addedUploadUrls: addedUploadUrls.join(','),
|
||||
});
|
||||
@ -159,24 +147,28 @@ export const addAllUploadsAction = async ({
|
||||
fileBytes,
|
||||
);
|
||||
if (updatedUrl) {
|
||||
const subhead = 'Adding to database';
|
||||
stream.update({
|
||||
headline,
|
||||
subhead: 'Adding to database',
|
||||
subhead,
|
||||
addedUploadUrls: addedUploadUrls.join(','),
|
||||
});
|
||||
const photo = convertFormDataToPhotoDbInsert(form);
|
||||
photo.url = updatedUrl;
|
||||
await insertPhoto(photo);
|
||||
addedUploadUrls.push(url);
|
||||
// Re-submit with updated url
|
||||
stream.update({
|
||||
subhead,
|
||||
addedUploadUrls: addedUploadUrls.join(','),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch (error: any) {
|
||||
// eslint-disable-next-line max-len
|
||||
stream.error(`${error.message} (${addedUploadUrls.length} of ${uploadTotal} photos successfully added)`);
|
||||
} finally {
|
||||
revalidateAllKeysAndPaths();
|
||||
}
|
||||
revalidateAllKeysAndPaths();
|
||||
stream.done();
|
||||
})();
|
||||
|
||||
|
||||
@ -97,8 +97,8 @@
|
||||
@apply
|
||||
text-invert
|
||||
bg-gray-900 dark:bg-gray-100
|
||||
disabled:text-gray-300 disabled:dark:text-gray-700
|
||||
disabled:bg-white disabled:dark:bg-black
|
||||
disabled:text-dim
|
||||
disabled:bg-gray-100 dark:disabled:bg-gray-900
|
||||
disabled:border-gray-200 disabled:dark:border-gray-700
|
||||
border-gray-900 dark:border-gray-100
|
||||
active:bg-gray-700 active:border-gray-700
|
||||
|
||||
Loading…
Reference in New Issue
Block a user