From 5f028a3b077a9f11dd535f7c809e4504d9d2a6ce Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 6 Jul 2024 12:42:26 -0500 Subject: [PATCH 1/7] Update photo install visualization --- src/admin/AdminAddAllUploads.tsx | 48 ++++--- src/admin/AdminPhotosClient.tsx | 13 +- src/admin/AdminUploadsClient.tsx | 24 +++- src/admin/AdminUploadsTable.tsx | 176 ++++++++++++++------------ src/photo/actions.ts | 21 +-- src/services/storage/aws-s3.ts | 1 + src/services/storage/cloudflare-r2.ts | 1 + src/services/storage/index.ts | 1 + src/services/storage/vercel-blob.ts | 2 + 9 files changed, 162 insertions(+), 125 deletions(-) diff --git a/src/admin/AdminAddAllUploads.tsx b/src/admin/AdminAddAllUploads.tsx index ffe69082..bb5a3003 100644 --- a/src/admin/AdminAddAllUploads.tsx +++ b/src/admin/AdminAddAllUploads.tsx @@ -21,6 +21,7 @@ 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'; +import { AddedUrlStatus } from './AdminUploadsClient'; const UPLOAD_BATCH_SIZE = 4; @@ -29,18 +30,17 @@ export default function AdminAddAllUploads({ uniqueTags, isAdding, setIsAdding, - setAddedUploadUrls, + setAddedUrlStatuses, }: { storageUrls: string[] uniqueTags?: Tags isAdding: boolean setIsAdding: (isAdding: boolean) => void - setAddedUploadUrls?: Dispatch> + setAddedUrlStatuses: Dispatch> }) { const divRef = useRef(null); const [buttonText, setButtonText] = useState('Add All Uploads'); - const [buttonSubheadText, setButtonSubheadText] = useState(''); const [showTags, setShowTags] = useState(false); const [tags, setTags] = useState(''); const [actionErrorMessage, setActionErrorMessage] = useState(''); @@ -50,7 +50,7 @@ export default function AdminAddAllUploads({ const router = useRouter(); - const addedUploadUrls = useRef([]); + const addedUploadCount = useRef(0); const addUploadUrls = async (uploadUrls: string[]) => { try { const stream = await addAllUploadsAction({ @@ -60,23 +60,31 @@ export default function AdminAddAllUploads({ 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}` + setButtonText(addedUploadCount.current === 0 + ? `Adding 1 of ${storageUrls.length}` + : `Adding ${addedUploadCount.current + 1} 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; + setAddedUrlStatuses(current => { + const update = current.map(status => + status.url === data?.url + ? { + ...status, + // Prevent status regressions + status: status.status !== 'added' ? data.status : 'added', + statusMessage: data.statusMessage, + progress: data.progress, + } + : status + ); + addedUploadCount.current = update + .filter(({ status }) => status === 'added') + .length; + return update; }); setAddingProgress((current = 0) => { const updatedProgress = ( ( - ((addedUploadUrls.current.length || 1) - 1) + + ((addedUploadCount.current || 1) - 1) + (data?.progress ?? 0) ) / storageUrls.length @@ -158,6 +166,9 @@ export default function AdminAddAllUploads({ // eslint-disable-next-line max-len if (confirm(`Are you sure you want to add all ${storageUrls.length} uploads?`)) { setIsAdding(true); + setAddedUrlStatuses(current => current.map((url, index) => + index === 0 ? { ...url, status: 'adding' } : url + )); const uploadsToAdd = storageUrls.slice(); try { while (uploadsToAdd.length > 0) { @@ -166,7 +177,6 @@ export default function AdminAddAllUploads({ ); } setButtonText('Complete'); - setButtonSubheadText('All uploads added'); setAddingProgress(1); setIsAdding(false); setIsAddingComplete(true); @@ -184,10 +194,6 @@ export default function AdminAddAllUploads({ > {buttonText} - {buttonSubheadText && -
- {buttonSubheadText} -
} diff --git a/src/admin/AdminPhotosClient.tsx b/src/admin/AdminPhotosClient.tsx index ac63fd2e..9ef063d9 100644 --- a/src/admin/AdminPhotosClient.tsx +++ b/src/admin/AdminPhotosClient.tsx @@ -3,7 +3,6 @@ import PhotoUpload from '@/photo/PhotoUpload'; import { clsx } from 'clsx/lite'; import SiteGrid from '@/components/SiteGrid'; -import AdminUploadsTable from '@/admin/AdminUploadsTable'; import { AI_TEXT_GENERATION_ENABLED, PRO_MODE_ENABLED } from '@/site/config'; import AdminPhotosTable from '@/admin/AdminPhotosTable'; import AdminPhotosTableInfinite from '@/admin/AdminPhotosTableInfinite'; @@ -13,6 +12,7 @@ import { Photo } from '@/photo'; import { StorageListResponse } from '@/services/storage'; import { useState } from 'react'; import { LiaBroomSolid } from 'react-icons/lia'; +import AdminUploadsTable from './AdminUploadsTable'; export default function AdminPhotosClient({ photos, @@ -58,15 +58,16 @@ export default function AdminPhotosClient({ {photosCountOutdated} } - {!isUploading && blobPhotoUrls.length > 0 && + {blobPhotoUrls.length > 0 &&
- +
+ Photo Blobs ({blobPhotoUrls.length}) +
+
} {/* Use custom spacing to address gap/space-y compatibility quirks */}
diff --git a/src/admin/AdminUploadsClient.tsx b/src/admin/AdminUploadsClient.tsx index f93b2cac..484b84f8 100644 --- a/src/admin/AdminUploadsClient.tsx +++ b/src/admin/AdminUploadsClient.tsx @@ -2,21 +2,33 @@ import { StorageListResponse } from '@/services/storage'; import AdminAddAllUploads from './AdminAddAllUploads'; -import AdminUploadsTable from './AdminUploadsTable'; import { useState } from 'react'; import { Tags } from '@/tag'; +import AdminUploadsTable from './AdminUploadsTable'; + +export type AddedUrlStatus = { + url: string + uploadedAt?: Date + status?: 'waiting' | 'adding' | 'added' + statusMessage?: string + progress?: number +}; export default function AdminUploadsClient({ - title, urls, uniqueTags, }: { - title?: string urls: StorageListResponse uniqueTags?: Tags }) { const [isAdding, setIsAdding] = useState(false); - const [addedUploadUrls, setAddedUploadUrls] = useState([]); + const [addedUrlStatuses, setAddedUrlStatuses] = + useState(urls.map(({ url, uploadedAt }) => ({ + url, + uploadedAt, + status: 'waiting', + }))); + return (
{urls.length > 1 && @@ -25,9 +37,9 @@ export default function AdminUploadsClient({ uniqueTags={uniqueTags} isAdding={isAdding} setIsAdding={setIsAdding} - setAddedUploadUrls={setAddedUploadUrls} + setAddedUrlStatuses={setAddedUrlStatuses} />} - +
); } diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index bf6f2d1a..ec634ca8 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -1,99 +1,109 @@ -import { Fragment } from 'react'; -import AdminTable from './AdminTable'; -import Link from 'next/link'; -import { - StorageListResponse, - fileNameForStorageUrl, - getIdFromStorageUrl, -} from '@/services/storage'; +'use client'; + +import ImageSmall from '@/components/image/ImageSmall'; +import Spinner from '@/components/Spinner'; +import { getIdFromStorageUrl } from '@/services/storage'; +import { clsx } from 'clsx/lite'; +import { motion } from 'framer-motion'; +import { FaRegCircleCheck } from 'react-icons/fa6'; +import { pathForAdminUploadUrl } from '@/site/paths'; +import AddButton from './AddButton'; import FormWithConfirm from '@/components/FormWithConfirm'; import { deleteBlobPhotoAction } from '@/photo/actions'; import DeleteButton from './DeleteButton'; -import { clsx } from 'clsx/lite'; -import { pathForAdminUploadUrl } from '@/site/paths'; -import AddButton from './AddButton'; -import { formatDate } from 'date-fns'; -import ImageSmall from '@/components/image/ImageSmall'; -import { FaRegCircleCheck } from 'react-icons/fa6'; -import Spinner from '@/components/Spinner'; +import { formatDate } from '@/utility/date'; +import { AddedUrlStatus } from './AdminUploadsClient'; export default function AdminUploadsTable({ - title, - urls, - addedUploadUrls, isAdding, + urls, }: { - title?: string - urls: StorageListResponse - addedUploadUrls?: string[] isAdding?: boolean + urls: AddedUrlStatus[] }) { + const isComplete = urls.every(({ status }) => status === 'added'); + return ( - - {urls.map(({ url, uploadedAt }) => { +
+ {urls.map(({ url, status, statusMessage, uploadedAt }) => { const addUploadPath = pathForAdminUploadUrl(url); - const uploadFileName = fileNameForStorageUrl(url); - const uploadId = getIdFromStorageUrl(url); - return - - - - - {uploadId} - + return
- {addedUploadUrls?.includes(url) || isAdding - ? - {addedUploadUrls?.includes(url) - ? - : } + + + +
{getIdFromStorageUrl(url)}
+
+ {isAdding || isComplete + ? status === 'added' + ? 'Complete' + : status === 'adding' + ? statusMessage ?? 'Adding ...' + : 'Waiting' + : uploadedAt + ? + {formatDate(uploadedAt, 'medium')} + + : '—'} +
- : <> - - - - - - - } +
+ + {isAdding || isComplete + ? <> + {status === 'added' + ? + : status === 'adding' + ? + : + — + } + + : <> + + + + + + + } +
- ;})} - +
; + })} +
); -} +} \ No newline at end of file diff --git a/src/photo/actions.ts b/src/photo/actions.ts index 71abf6cb..663cd916 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -46,6 +46,7 @@ import { import { generateAiImageQueries } from './ai/server'; import { createStreamableValue } from 'ai/rsc'; import { convertUploadToPhoto } from './storage'; +import { AddedUrlStatus } from '@/admin/AdminUploadsClient'; // Private actions @@ -84,24 +85,26 @@ export const addAllUploadsAction = async ({ const PROGRESS_TASK_COUNT = AI_TEXT_GENERATION_ENABLED ? 5 : 4; const addedUploadUrls: string[] = []; + let currentUploadUrl = ''; let progress = 0; - const stream = createStreamableValue<{ - subhead: string - addedUploadUrls: string - progress: number - }>(); + const stream = createStreamableValue(); - const streamUpdate = (subhead: string) => + const streamUpdate = ( + statusMessage: string, + status: AddedUrlStatus['status'] = 'adding', + ) => stream.update({ - subhead, - addedUploadUrls: addedUploadUrls.join(','), + url: currentUploadUrl, + status, + statusMessage, progress: ++progress / PROGRESS_TASK_COUNT, }); (async () => { try { for (const url of uploadUrls) { + currentUploadUrl = url; progress = 0; streamUpdate('Parsing EXIF data'); @@ -156,7 +159,7 @@ export const addAllUploadsAction = async ({ await insertPhoto(photo); addedUploadUrls.push(url); // Re-submit with updated url - streamUpdate(subheadFinal); + streamUpdate(subheadFinal, 'added'); } } }; diff --git a/src/services/storage/aws-s3.ts b/src/services/storage/aws-s3.ts index 9e7e2fa1..63884a68 100644 --- a/src/services/storage/aws-s3.ts +++ b/src/services/storage/aws-s3.ts @@ -72,6 +72,7 @@ export const awsS3List = async ( })) .then((data) => data.Contents?.map(({ Key, LastModified }) => ({ url: urlForKey(Key), + fileName: Key ?? '', uploadedAt: LastModified, })) ?? []); diff --git a/src/services/storage/cloudflare-r2.ts b/src/services/storage/cloudflare-r2.ts index 12a45572..8f3f80eb 100644 --- a/src/services/storage/cloudflare-r2.ts +++ b/src/services/storage/cloudflare-r2.ts @@ -91,6 +91,7 @@ export const cloudflareR2List = async ( })) .then((data) => data.Contents?.map(({ Key, LastModified }) => ({ url: urlForKey(Key), + fileName: Key ?? '', uploadedAt: LastModified, })) ?? []); diff --git a/src/services/storage/index.ts b/src/services/storage/index.ts index 7fb3123f..e773b3b9 100644 --- a/src/services/storage/index.ts +++ b/src/services/storage/index.ts @@ -35,6 +35,7 @@ export const generateStorageId = () => generateNanoid(16); export type StorageListResponse = { url: string + fileName: string uploadedAt?: Date }[]; diff --git a/src/services/storage/vercel-blob.ts b/src/services/storage/vercel-blob.ts index 60cfdd22..1955488b 100644 --- a/src/services/storage/vercel-blob.ts +++ b/src/services/storage/vercel-blob.ts @@ -1,6 +1,7 @@ import { PATH_API_VERCEL_BLOB_UPLOAD } from '@/site/paths'; import { copy, del, list, put } from '@vercel/blob'; import { upload } from '@vercel/blob/client'; +import { fileNameForStorageUrl } from '.'; const VERCEL_BLOB_STORE_ID = process.env.BLOB_READ_WRITE_TOKEN?.match( /^vercel_blob_rw_([a-z0-9]+)_[a-z0-9]+$/i, @@ -58,5 +59,6 @@ export const vercelBlobDelete = (fileName: string) => del(fileName); export const vercelBlobList = (prefix: string) => list({ prefix }) .then(({ blobs }) => blobs.map(({ url, uploadedAt }) => ({ url, + fileName: fileNameForStorageUrl(url), uploadedAt, }))); From 3b5b8fc559ab4cb01bb2f7c74ce1c8dd2458cccc Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 6 Jul 2024 12:57:30 -0500 Subject: [PATCH 2/7] Fix mobile upload styles --- src/admin/AdminUploadsTable.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index ec634ca8..f8e1b764 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -11,8 +11,8 @@ import AddButton from './AddButton'; import FormWithConfirm from '@/components/FormWithConfirm'; import { deleteBlobPhotoAction } from '@/photo/actions'; import DeleteButton from './DeleteButton'; -import { formatDate } from '@/utility/date'; import { AddedUrlStatus } from './AdminUploadsClient'; +import ResponsiveDate from '@/components/ResponsiveDate'; export default function AdminUploadsTable({ isAdding, @@ -46,11 +46,13 @@ export default function AdminUploadsTable({ src={url} alt={url} aspectRatio={3.0 / 2.0} - className="rounded-sm overflow-hidden" + className="rounded-[3px] overflow-hidden shrink-0" /> - -
{getIdFromStorageUrl(url)}
-
+ +
+ {getIdFromStorageUrl(url)} +
+
{isAdding || isComplete ? status === 'added' ? 'Complete' @@ -58,9 +60,7 @@ export default function AdminUploadsTable({ ? statusMessage ?? 'Adding ...' : 'Waiting' : uploadedAt - ? - {formatDate(uploadedAt, 'medium')} - + ? : '—'}
From c2f9ddf6b61e57aa61d33519fa56fd8f733f5f68 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 6 Jul 2024 14:04:03 -0500 Subject: [PATCH 3/7] Streamline url add api --- src/admin/AdminAddAllUploads.tsx | 15 ++++++++------- src/admin/AdminUploadsClient.tsx | 29 +++++++++++++---------------- src/admin/AdminUploadsTable.tsx | 24 +++++++++++------------- src/photo/actions.ts | 8 ++++---- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/admin/AdminAddAllUploads.tsx b/src/admin/AdminAddAllUploads.tsx index bb5a3003..26e28028 100644 --- a/src/admin/AdminAddAllUploads.tsx +++ b/src/admin/AdminAddAllUploads.tsx @@ -21,7 +21,7 @@ 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'; -import { AddedUrlStatus } from './AdminUploadsClient'; +import { UrlAddStatus } from './AdminUploadsClient'; const UPLOAD_BATCH_SIZE = 4; @@ -30,13 +30,13 @@ export default function AdminAddAllUploads({ uniqueTags, isAdding, setIsAdding, - setAddedUrlStatuses, + setUrlAddStatuses, }: { storageUrls: string[] uniqueTags?: Tags isAdding: boolean setIsAdding: (isAdding: boolean) => void - setAddedUrlStatuses: Dispatch> + setUrlAddStatuses: Dispatch> }) { const divRef = useRef(null); @@ -64,7 +64,7 @@ export default function AdminAddAllUploads({ ? `Adding 1 of ${storageUrls.length}` : `Adding ${addedUploadCount.current + 1} of ${storageUrls.length}` ); - setAddedUrlStatuses(current => { + setUrlAddStatuses(current => { const update = current.map(status => status.url === data?.url ? { @@ -166,9 +166,10 @@ export default function AdminAddAllUploads({ // eslint-disable-next-line max-len if (confirm(`Are you sure you want to add all ${storageUrls.length} uploads?`)) { setIsAdding(true); - setAddedUrlStatuses(current => current.map((url, index) => - index === 0 ? { ...url, status: 'adding' } : url - )); + setUrlAddStatuses(current => current.map((url, index) => ({ + ...url, + status: index === 0 ? 'adding' : 'waiting', + }))); const uploadsToAdd = storageUrls.slice(); try { while (uploadsToAdd.length > 0) { diff --git a/src/admin/AdminUploadsClient.tsx b/src/admin/AdminUploadsClient.tsx index 484b84f8..77ab233e 100644 --- a/src/admin/AdminUploadsClient.tsx +++ b/src/admin/AdminUploadsClient.tsx @@ -2,11 +2,11 @@ import { StorageListResponse } from '@/services/storage'; import AdminAddAllUploads from './AdminAddAllUploads'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import { Tags } from '@/tag'; import AdminUploadsTable from './AdminUploadsTable'; -export type AddedUrlStatus = { +export type UrlAddStatus = { url: string uploadedAt?: Date status?: 'waiting' | 'adding' | 'added' @@ -22,24 +22,21 @@ export default function AdminUploadsClient({ uniqueTags?: Tags }) { const [isAdding, setIsAdding] = useState(false); - const [addedUrlStatuses, setAddedUrlStatuses] = - useState(urls.map(({ url, uploadedAt }) => ({ - url, - uploadedAt, - status: 'waiting', - }))); + const [urlAddStatuses, setUrlAddStatuses] = useState(urls); + + const storageUrls = useMemo(() => urls.map(({ url }) => url), [urls]); return (
{urls.length > 1 && - url)} - uniqueTags={uniqueTags} - isAdding={isAdding} - setIsAdding={setIsAdding} - setAddedUrlStatuses={setAddedUrlStatuses} - />} - + } +
); } diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index f8e1b764..0d83a794 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -11,21 +11,21 @@ import AddButton from './AddButton'; import FormWithConfirm from '@/components/FormWithConfirm'; import { deleteBlobPhotoAction } from '@/photo/actions'; import DeleteButton from './DeleteButton'; -import { AddedUrlStatus } from './AdminUploadsClient'; +import { UrlAddStatus } from './AdminUploadsClient'; import ResponsiveDate from '@/components/ResponsiveDate'; export default function AdminUploadsTable({ isAdding, - urls, + urlAddStatuses, }: { isAdding?: boolean - urls: AddedUrlStatus[] + urlAddStatuses: UrlAddStatus[] }) { - const isComplete = urls.every(({ status }) => status === 'added'); + const isComplete = urlAddStatuses.every(({ status }) => status === 'added'); return (
- {urls.map(({ url, status, statusMessage, uploadedAt }) => { + {urlAddStatuses.map(({ url, status, statusMessage, uploadedAt }) => { const addUploadPath = pathForAdminUploadUrl(url); return
{isAdding || isComplete ? status === 'added' - ? 'Complete' + ? 'Added' : status === 'adding' ? statusMessage ?? 'Adding ...' : 'Waiting' @@ -88,7 +86,7 @@ export default function AdminUploadsTable({ (); + const stream = createStreamableValue(); const streamUpdate = ( statusMessage: string, - status: AddedUrlStatus['status'] = 'adding', + status: UrlAddStatus['status'] = 'adding', ) => stream.update({ url: currentUploadUrl, @@ -144,7 +144,7 @@ export const addAllUploadsAction = async ({ takenAtNaive: photoFormExif.takenAtNaive || takenAtNaiveLocal, }; - streamUpdate('Moving upload to photo storage'); + streamUpdate('Transferring to photo storage'); const updatedUrl = await convertUploadToPhoto({ urlOrigin: url, From fb0b6721df671fcceb4825a9dbd6c28b9de57c92 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 6 Jul 2024 17:09:15 -0500 Subject: [PATCH 4/7] Fix prop label --- src/admin/AdminPhotosClient.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/admin/AdminPhotosClient.tsx b/src/admin/AdminPhotosClient.tsx index 9ef063d9..8c82d61c 100644 --- a/src/admin/AdminPhotosClient.tsx +++ b/src/admin/AdminPhotosClient.tsx @@ -67,7 +67,7 @@ export default function AdminPhotosClient({
Photo Blobs ({blobPhotoUrls.length})
- +
} {/* Use custom spacing to address gap/space-y compatibility quirks */}
From 867176775c53d73b8eaa24d9039795ed2a16773d Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 6 Jul 2024 20:30:03 -0500 Subject: [PATCH 5/7] Switch upload animation to wobble --- src/admin/AdminUploadsTable.tsx | 53 +++++++++++++++++++-------------- src/photo/PhotoSmall.tsx | 2 +- src/site/globals.css | 5 ++++ tailwind.config.js | 10 +++++++ 4 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index 0d83a794..ca1e1126 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -4,7 +4,6 @@ import ImageSmall from '@/components/image/ImageSmall'; import Spinner from '@/components/Spinner'; import { getIdFromStorageUrl } from '@/services/storage'; import { clsx } from 'clsx/lite'; -import { motion } from 'framer-motion'; import { FaRegCircleCheck } from 'react-icons/fa6'; import { pathForAdminUploadUrl } from '@/site/paths'; import AddButton from './AddButton'; @@ -25,27 +24,38 @@ export default function AdminUploadsTable({ return (
- {urlAddStatuses.map(({ url, status, statusMessage, uploadedAt }) => { - const addUploadPath = pathForAdminUploadUrl(url); - return
+ {urlAddStatuses.map(({ url, status, statusMessage, uploadedAt }) => +
- - +
+ +
{getIdFromStorageUrl(url)} @@ -62,7 +72,7 @@ export default function AdminUploadsTable({ : '—'}
-
+
{isAdding || isComplete ? <> @@ -78,7 +88,7 @@ export default function AdminUploadsTable({ } : <> - + }
-
; - })} +
)}
); } \ No newline at end of file diff --git a/src/photo/PhotoSmall.tsx b/src/photo/PhotoSmall.tsx index 282685f5..ac08a88a 100644 --- a/src/photo/PhotoSmall.tsx +++ b/src/photo/PhotoSmall.tsx @@ -44,7 +44,7 @@ export default function PhotoSmall({ selected && 'brightness-50', 'min-w-[50px]', 'rounded-[3px] overflow-hidden', - 'border border-gray-200 dark:border-gray-800', + 'border-subtle', )} prefetch={prefetch} > diff --git a/src/site/globals.css b/src/site/globals.css index 50eaa560..a522d8a8 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -150,6 +150,11 @@ @apply text-red-500 dark:text-red-400 } + /* Utilities: Border */ + .border-subtle { + @apply + border border-gray-200 dark:border-gray-800 + } /* Utilities: Background */ .bg-main { @apply diff --git a/tailwind.config.js b/tailwind.config.js index 23b349c6..8831b5bf 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -27,6 +27,8 @@ module.exports = { animation: { 'rotate-pulse': 'rotate-pulse 0.75s linear infinite normal both running', + 'hover-wobble': + 'hover-wobble 6s linear infinite normal both running', }, keyframes: { 'rotate-pulse': { @@ -34,6 +36,14 @@ module.exports = { '50%': { transform: 'rotate(180deg) scale(0.8)' }, '100%': { transform: 'rotate(360deg) scale(1)' }, }, + 'hover-wobble': { + '0%': { transform: 'rotate(0deg)' }, + '20%': { transform: 'rotate(3.5deg)' }, + '40%': { transform: 'rotate(-2deg)' }, + '60%': { transform: 'rotate(2.5deg)' }, + '80%': { transform: 'rotate(-2.5deg)' }, + '100%': { transform: 'rotate(0deg)' }, + }, }, }, }, From b4c0e0adb9a5684ffaa2172f056db1e5e098ff73 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 6 Jul 2024 22:51:54 -0500 Subject: [PATCH 6/7] Switch from upload wobble to drift --- src/admin/AdminUploadsClient.tsx | 2 +- src/admin/AdminUploadsTable.tsx | 2 +- tailwind.config.js | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/admin/AdminUploadsClient.tsx b/src/admin/AdminUploadsClient.tsx index 77ab233e..9b48061e 100644 --- a/src/admin/AdminUploadsClient.tsx +++ b/src/admin/AdminUploadsClient.tsx @@ -28,7 +28,7 @@ export default function AdminUploadsClient({ return (
- {urls.length > 1 && + {(urls.length > 1 || isAdding) &&
diff --git a/tailwind.config.js b/tailwind.config.js index 8831b5bf..aa7a96fe 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -27,6 +27,8 @@ module.exports = { animation: { 'rotate-pulse': 'rotate-pulse 0.75s linear infinite normal both running', + 'hover-drift': + 'hover-drift 8s linear infinite', 'hover-wobble': 'hover-wobble 6s linear infinite normal both running', }, @@ -36,6 +38,14 @@ module.exports = { '50%': { transform: 'rotate(180deg) scale(0.8)' }, '100%': { transform: 'rotate(360deg) scale(1)' }, }, + 'hover-drift': { + '0%': { transform: 'translate(0, 0)' }, + '20%': { transform: 'translate(1px, -2px)' }, + '40%': { transform: 'translate(1px, 1.5px)' }, + '60%': { transform: 'translate(-1px, 2px)' }, + '80%': { transform: 'translate(-1.5px, -1.75px)' }, + '100%': { transform: 'translate(0, 0)' }, + }, 'hover-wobble': { '0%': { transform: 'rotate(0deg)' }, '20%': { transform: 'rotate(3.5deg)' }, From 2f029f796c573d9fd2725027eb3440084cabdfe1 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sat, 6 Jul 2024 23:02:30 -0500 Subject: [PATCH 7/7] Reduce opacity for non-active uploads --- src/admin/AdminUploadsTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/admin/AdminUploadsTable.tsx b/src/admin/AdminUploadsTable.tsx index 14e06506..8d2796e4 100644 --- a/src/admin/AdminUploadsTable.tsx +++ b/src/admin/AdminUploadsTable.tsx @@ -33,7 +33,7 @@ export default function AdminUploadsTable({ className={clsx( 'flex items-center grow gap-2', 'transition-opacity', - isAdding && !isComplete && status !== 'adding' && 'opacity-40', + isAdding && !isComplete && status !== 'adding' && 'opacity-30', )} >