Memoize admin menus
This commit is contained in:
parent
ed9aad1d39
commit
aa43084212
@ -16,7 +16,7 @@ import { IoArrowDown, IoArrowUp, IoCloseSharp } from 'react-icons/io5';
|
|||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import AdminAppInfoIcon from './AdminAppInfoIcon';
|
import AdminAppInfoIcon from './AdminAppInfoIcon';
|
||||||
import { signOutAction } from '@/auth/actions';
|
import { signOutAction } from '@/auth/actions';
|
||||||
import { ComponentProps } from 'react';
|
import { ComponentProps, useMemo } from 'react';
|
||||||
import useIsKeyBeingPressed from '@/utility/useIsKeyBeingPressed';
|
import useIsKeyBeingPressed from '@/utility/useIsKeyBeingPressed';
|
||||||
import IconPhoto from '@/components/icons/IconPhoto';
|
import IconPhoto from '@/components/icons/IconPhoto';
|
||||||
import IconUpload from '@/components/icons/IconUpload';
|
import IconUpload from '@/components/icons/IconUpload';
|
||||||
@ -63,133 +63,149 @@ export default function AdminAppMenu({
|
|||||||
|
|
||||||
const showAppInsightsLink = photosCountTotal > 0 && !isAltPressed;
|
const showAppInsightsLink = photosCountTotal > 0 && !isAltPressed;
|
||||||
|
|
||||||
const sectionUpload: ComponentProps<typeof MoreMenuItem>[] = [];
|
const sectionUpload: ComponentProps<typeof MoreMenuItem>[] =
|
||||||
const sectionMain: ComponentProps<typeof MoreMenuItem>[] = [];
|
useMemo(() => ([{
|
||||||
const sectionSignOut: ComponentProps<typeof MoreMenuItem>[] = [];
|
label: 'Upload Photos',
|
||||||
|
icon: <IconUpload
|
||||||
sectionUpload.push({
|
|
||||||
label: 'Upload Photos',
|
|
||||||
icon: <IconUpload
|
|
||||||
size={15}
|
|
||||||
className="translate-x-[0.5px] translate-y-[0.5px]"
|
|
||||||
/>,
|
|
||||||
annotation: isLoadingAdminData &&
|
|
||||||
<Spinner className="translate-y-[1.5px]" />,
|
|
||||||
action: startUpload,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (uploadsCount) {
|
|
||||||
sectionMain.push({
|
|
||||||
label: 'Uploads',
|
|
||||||
annotation: `${uploadsCount}`,
|
|
||||||
icon: <IconFolder
|
|
||||||
size={16}
|
|
||||||
className="translate-x-[1px] translate-y-[1px]"
|
|
||||||
/>,
|
|
||||||
href: PATH_ADMIN_UPLOADS,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (photosCountNeedSync) {
|
|
||||||
sectionMain.push({
|
|
||||||
label: 'Updates',
|
|
||||||
annotation: <>
|
|
||||||
<span className="mr-3">
|
|
||||||
{photosCountNeedSync}
|
|
||||||
</span>
|
|
||||||
<InsightsIndicatorDot
|
|
||||||
className="inline-block translate-y-[-1px]"
|
|
||||||
size="small"
|
|
||||||
/>
|
|
||||||
</>,
|
|
||||||
icon: <IconBroom
|
|
||||||
size={18}
|
|
||||||
className="translate-y-[-0.5px]"
|
|
||||||
/>,
|
|
||||||
href: PATH_ADMIN_PHOTOS_UPDATES,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (photosCountTotal) {
|
|
||||||
sectionMain.push({
|
|
||||||
label: 'Manage Photos',
|
|
||||||
...photosCountTotal && {
|
|
||||||
annotation: `${photosCountTotal}`,
|
|
||||||
},
|
|
||||||
icon: <IconPhoto
|
|
||||||
size={15}
|
size={15}
|
||||||
className="translate-x-[-0.5px] translate-y-[1px]"
|
className="translate-x-[0.5px] translate-y-[0.5px]"
|
||||||
/>,
|
/>,
|
||||||
href: PATH_ADMIN_PHOTOS,
|
annotation: isLoadingAdminData &&
|
||||||
});
|
<Spinner className="translate-y-[1.5px]" />,
|
||||||
}
|
action: startUpload,
|
||||||
if (tagsCount) {
|
}]), [isLoadingAdminData, startUpload]);
|
||||||
sectionMain.push({
|
|
||||||
label: 'Manage Tags',
|
const sectionMain: ComponentProps<typeof MoreMenuItem>[] = useMemo(() => {
|
||||||
annotation: `${tagsCount}`,
|
const items: ComponentProps<typeof MoreMenuItem>[] = [];
|
||||||
icon: <IconTag
|
|
||||||
size={15}
|
if (uploadsCount) {
|
||||||
className="translate-y-[1.5px]"
|
items.push({
|
||||||
/>,
|
label: 'Uploads',
|
||||||
href: PATH_ADMIN_TAGS,
|
annotation: `${uploadsCount}`,
|
||||||
});
|
icon: <IconFolder
|
||||||
}
|
|
||||||
if (recipesCount) {
|
|
||||||
sectionMain.push({
|
|
||||||
label: 'Manage Recipes',
|
|
||||||
annotation: `${recipesCount}`,
|
|
||||||
icon: <IconRecipe
|
|
||||||
size={17}
|
|
||||||
className="translate-x-[-0.5px] translate-y-[1px]"
|
|
||||||
/>,
|
|
||||||
href: PATH_ADMIN_RECIPES,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (photosCountTotal) {
|
|
||||||
sectionMain.push({
|
|
||||||
label: isSelecting
|
|
||||||
? 'Exit Batch Edit'
|
|
||||||
: 'Batch Edit ...',
|
|
||||||
icon: isSelecting
|
|
||||||
? <IoCloseSharp
|
|
||||||
size={18}
|
|
||||||
className="translate-x-[-1px] translate-y-[1px]"
|
|
||||||
/>
|
|
||||||
: <IoMdCheckboxOutline
|
|
||||||
size={16}
|
size={16}
|
||||||
className="translate-x-[-0.5px]"
|
className="translate-x-[1px] translate-y-[1px]"
|
||||||
/>,
|
/>,
|
||||||
href: PATH_GRID_INFERRED,
|
href: PATH_ADMIN_UPLOADS,
|
||||||
action: () => {
|
});
|
||||||
if (isSelecting) {
|
}
|
||||||
setSelectedPhotoIds?.(undefined);
|
if (photosCountNeedSync) {
|
||||||
} else {
|
items.push({
|
||||||
setSelectedPhotoIds?.([]);
|
label: 'Updates',
|
||||||
}
|
annotation: <>
|
||||||
if (document.activeElement instanceof HTMLElement) {
|
<span className="mr-3">
|
||||||
document.activeElement.blur();
|
{photosCountNeedSync}
|
||||||
}
|
</span>
|
||||||
},
|
<InsightsIndicatorDot
|
||||||
shouldPreventDefault: false,
|
className="inline-block translate-y-[-1px]"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
</>,
|
||||||
|
icon: <IconBroom
|
||||||
|
size={18}
|
||||||
|
className="translate-y-[-0.5px]"
|
||||||
|
/>,
|
||||||
|
href: PATH_ADMIN_PHOTOS_UPDATES,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (photosCountTotal) {
|
||||||
|
items.push({
|
||||||
|
label: 'Manage Photos',
|
||||||
|
...photosCountTotal && {
|
||||||
|
annotation: `${photosCountTotal}`,
|
||||||
|
},
|
||||||
|
icon: <IconPhoto
|
||||||
|
size={15}
|
||||||
|
className="translate-x-[-0.5px] translate-y-[1px]"
|
||||||
|
/>,
|
||||||
|
href: PATH_ADMIN_PHOTOS,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tagsCount) {
|
||||||
|
items.push({
|
||||||
|
label: 'Manage Tags',
|
||||||
|
annotation: `${tagsCount}`,
|
||||||
|
icon: <IconTag
|
||||||
|
size={15}
|
||||||
|
className="translate-y-[1.5px]"
|
||||||
|
/>,
|
||||||
|
href: PATH_ADMIN_TAGS,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (recipesCount) {
|
||||||
|
items.push({
|
||||||
|
label: 'Manage Recipes',
|
||||||
|
annotation: `${recipesCount}`,
|
||||||
|
icon: <IconRecipe
|
||||||
|
size={17}
|
||||||
|
className="translate-x-[-0.5px] translate-y-[1px]"
|
||||||
|
/>,
|
||||||
|
href: PATH_ADMIN_RECIPES,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (photosCountTotal) {
|
||||||
|
items.push({
|
||||||
|
label: isSelecting
|
||||||
|
? 'Exit Batch Edit'
|
||||||
|
: 'Batch Edit ...',
|
||||||
|
icon: isSelecting
|
||||||
|
? <IoCloseSharp
|
||||||
|
size={18}
|
||||||
|
className="translate-x-[-1px] translate-y-[1px]"
|
||||||
|
/>
|
||||||
|
: <IoMdCheckboxOutline
|
||||||
|
size={16}
|
||||||
|
className="translate-x-[-0.5px]"
|
||||||
|
/>,
|
||||||
|
href: PATH_GRID_INFERRED,
|
||||||
|
action: () => {
|
||||||
|
if (isSelecting) {
|
||||||
|
setSelectedPhotoIds?.(undefined);
|
||||||
|
} else {
|
||||||
|
setSelectedPhotoIds?.([]);
|
||||||
|
}
|
||||||
|
if (document.activeElement instanceof HTMLElement) {
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shouldPreventDefault: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
items.push({
|
||||||
|
label: showAppInsightsLink
|
||||||
|
? 'App Insights'
|
||||||
|
: 'App Configuration',
|
||||||
|
icon: <AdminAppInfoIcon
|
||||||
|
size="small"
|
||||||
|
className="translate-x-[-0.5px] translate-y-[0.5px]"
|
||||||
|
/>,
|
||||||
|
href: showAppInsightsLink
|
||||||
|
? PATH_ADMIN_INSIGHTS
|
||||||
|
: PATH_ADMIN_CONFIGURATION,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
sectionMain.push({
|
return items;
|
||||||
label: showAppInsightsLink
|
}, [
|
||||||
? 'App Insights'
|
isSelecting,
|
||||||
: 'App Configuration',
|
photosCountNeedSync,
|
||||||
icon: <AdminAppInfoIcon
|
photosCountTotal,
|
||||||
size="small"
|
recipesCount,
|
||||||
className="translate-x-[-0.5px] translate-y-[0.5px]"
|
setSelectedPhotoIds,
|
||||||
/>,
|
showAppInsightsLink,
|
||||||
href: showAppInsightsLink
|
tagsCount,
|
||||||
? PATH_ADMIN_INSIGHTS
|
uploadsCount,
|
||||||
: PATH_ADMIN_CONFIGURATION,
|
]);
|
||||||
});
|
|
||||||
|
|
||||||
sectionSignOut.push({
|
const sectionSignOut: ComponentProps<typeof MoreMenuItem>[] =
|
||||||
label: 'Sign Out',
|
useMemo(() => ([{
|
||||||
icon: <IconSignOut size={15} />,
|
label: 'Sign Out',
|
||||||
action: () => signOutAction().then(clearAuthStateAndRedirectIfNecessary),
|
icon: <IconSignOut size={15} />,
|
||||||
});
|
action: () => signOutAction().then(clearAuthStateAndRedirectIfNecessary),
|
||||||
|
}]), [clearAuthStateAndRedirectIfNecessary]);
|
||||||
|
|
||||||
|
const sections = useMemo(() =>
|
||||||
|
[sectionUpload, sectionMain, sectionSignOut]
|
||||||
|
, [sectionUpload, sectionMain, sectionSignOut]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MoreMenu
|
<MoreMenu
|
||||||
@ -232,11 +248,7 @@ export default function AdminAppMenu({
|
|||||||
'[&>*>*]:translate-y-[6px]',
|
'[&>*>*]:translate-y-[6px]',
|
||||||
!animateMenuClose && '[&>*>*]:duration-300',
|
!animateMenuClose && '[&>*>*]:duration-300',
|
||||||
)}
|
)}
|
||||||
sections={[
|
sections={sections}
|
||||||
sectionUpload,
|
|
||||||
sectionMain,
|
|
||||||
sectionSignOut,
|
|
||||||
]}
|
|
||||||
ariaLabel="Admin Menu"
|
ariaLabel="Admin Menu"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -46,8 +46,8 @@ export default function AdminPhotoMenu({
|
|||||||
const shouldRedirectFav = isPathFavs(path) && isFav;
|
const shouldRedirectFav = isPathFavs(path) && isFav;
|
||||||
const shouldRedirectDelete = pathForPhoto({ photo: photo.id }) === path;
|
const shouldRedirectDelete = pathForPhoto({ photo: photo.id }) === path;
|
||||||
|
|
||||||
const sections = useMemo(() => {
|
const sectionMain = useMemo(() => {
|
||||||
const sectionMain: ComponentProps<typeof MoreMenuItem>[] = [{
|
const items: ComponentProps<typeof MoreMenuItem>[] = [{
|
||||||
label: 'Edit',
|
label: 'Edit',
|
||||||
icon: <IconEdit
|
icon: <IconEdit
|
||||||
size={15}
|
size={15}
|
||||||
@ -57,7 +57,7 @@ export default function AdminPhotoMenu({
|
|||||||
...showKeyCommands && { keyCommand: KEY_COMMANDS.edit },
|
...showKeyCommands && { keyCommand: KEY_COMMANDS.edit },
|
||||||
}];
|
}];
|
||||||
if (includeFavorite) {
|
if (includeFavorite) {
|
||||||
sectionMain.push({
|
items.push({
|
||||||
label: isFav ? 'Unfavorite' : 'Favorite',
|
label: isFav ? 'Unfavorite' : 'Favorite',
|
||||||
icon: <IconFavs
|
icon: <IconFavs
|
||||||
size={14}
|
size={14}
|
||||||
@ -75,7 +75,7 @@ export default function AdminPhotoMenu({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
sectionMain.push({
|
items.push({
|
||||||
label: 'Download',
|
label: 'Download',
|
||||||
icon: <MdOutlineFileDownload
|
icon: <MdOutlineFileDownload
|
||||||
size={17}
|
size={17}
|
||||||
@ -85,7 +85,7 @@ export default function AdminPhotoMenu({
|
|||||||
hrefDownloadName: downloadFileNameForPhoto(photo),
|
hrefDownloadName: downloadFileNameForPhoto(photo),
|
||||||
...showKeyCommands && { keyCommand: KEY_COMMANDS.download },
|
...showKeyCommands && { keyCommand: KEY_COMMANDS.download },
|
||||||
});
|
});
|
||||||
sectionMain.push({
|
items.push({
|
||||||
label: 'Sync',
|
label: 'Sync',
|
||||||
labelComplex: <span className="inline-flex items-center gap-2">
|
labelComplex: <span className="inline-flex items-center gap-2">
|
||||||
<span>Sync</span>
|
<span>Sync</span>
|
||||||
@ -103,32 +103,8 @@ export default function AdminPhotoMenu({
|
|||||||
.then(() => revalidatePhoto?.(photo.id)),
|
.then(() => revalidatePhoto?.(photo.id)),
|
||||||
...showKeyCommands && { keyCommand: KEY_COMMANDS.sync },
|
...showKeyCommands && { keyCommand: KEY_COMMANDS.sync },
|
||||||
});
|
});
|
||||||
const sectionDelete: ComponentProps<typeof MoreMenuItem>[] = [{
|
|
||||||
label: 'Delete',
|
return items;
|
||||||
icon: <BiTrash
|
|
||||||
size={15}
|
|
||||||
className="translate-x-[-1px]"
|
|
||||||
/>,
|
|
||||||
className: 'text-error *:hover:text-error',
|
|
||||||
color: 'red',
|
|
||||||
action: () => {
|
|
||||||
if (confirm(deleteConfirmationTextForPhoto(photo))) {
|
|
||||||
return deletePhotoAction(
|
|
||||||
photo.id,
|
|
||||||
photo.url,
|
|
||||||
shouldRedirectDelete,
|
|
||||||
).then(() => {
|
|
||||||
revalidatePhoto?.(photo.id, true);
|
|
||||||
registerAdminUpdate?.();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...showKeyCommands && {
|
|
||||||
keyCommandModifier: KEY_COMMANDS.delete[0],
|
|
||||||
keyCommand: KEY_COMMANDS.delete[1],
|
|
||||||
},
|
|
||||||
}];
|
|
||||||
return [sectionMain, sectionDelete];
|
|
||||||
}, [
|
}, [
|
||||||
photo,
|
photo,
|
||||||
showKeyCommands,
|
showKeyCommands,
|
||||||
@ -136,15 +112,49 @@ export default function AdminPhotoMenu({
|
|||||||
isFav,
|
isFav,
|
||||||
shouldRedirectFav,
|
shouldRedirectFav,
|
||||||
revalidatePhoto,
|
revalidatePhoto,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const sectionDelete: ComponentProps<typeof MoreMenuItem>[] = useMemo(() => [{
|
||||||
|
label: 'Delete',
|
||||||
|
icon: <BiTrash
|
||||||
|
size={15}
|
||||||
|
className="translate-x-[-1px]"
|
||||||
|
/>,
|
||||||
|
className: 'text-error *:hover:text-error',
|
||||||
|
color: 'red',
|
||||||
|
action: () => {
|
||||||
|
if (confirm(deleteConfirmationTextForPhoto(photo))) {
|
||||||
|
return deletePhotoAction(
|
||||||
|
photo.id,
|
||||||
|
photo.url,
|
||||||
|
shouldRedirectDelete,
|
||||||
|
).then(() => {
|
||||||
|
revalidatePhoto?.(photo.id, true);
|
||||||
|
registerAdminUpdate?.();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...showKeyCommands && {
|
||||||
|
keyCommandModifier: KEY_COMMANDS.delete[0],
|
||||||
|
keyCommand: KEY_COMMANDS.delete[1],
|
||||||
|
},
|
||||||
|
}], [
|
||||||
|
photo,
|
||||||
|
showKeyCommands,
|
||||||
|
revalidatePhoto,
|
||||||
shouldRedirectDelete,
|
shouldRedirectDelete,
|
||||||
registerAdminUpdate,
|
registerAdminUpdate,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const sections = useMemo(() =>
|
||||||
|
[sectionMain, sectionDelete]
|
||||||
|
, [sectionMain, sectionDelete]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
isUserSignedIn
|
isUserSignedIn
|
||||||
? <MoreMenu {...{
|
? <MoreMenu {...{
|
||||||
sections,
|
|
||||||
...props,
|
...props,
|
||||||
|
sections,
|
||||||
}}/>
|
}}/>
|
||||||
: null
|
: null
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user