diff --git a/src/admin/AdminPhotoMenuClient.tsx b/src/admin/AdminPhotoMenuClient.tsx index e75de192..c90fddb0 100644 --- a/src/admin/AdminPhotoMenuClient.tsx +++ b/src/admin/AdminPhotoMenuClient.tsx @@ -3,16 +3,38 @@ import { ComponentProps } from 'react'; import { pathForAdminPhotoEdit } from '@/site/paths'; import MoreMenu from '../components/MoreMenu'; +import { toggleFavoritePhoto } from '@/photo/actions'; +import { FaRegEdit, FaStar } from 'react-icons/fa'; +import { Photo } from '@/photo'; +import { isPathFavs, isPhotoFav } from '@/tag'; +import clsx from 'clsx/lite'; +import { usePathname } from 'next/navigation'; export default function AdminPhotoMenuClient({ - photoId, + photo, ...props }: Omit, 'items'> & { - photoId: string + photo: Photo }) { + const isFav = isPhotoFav(photo); + const path = usePathname(); + const shouldRedirect = isPathFavs(path) && isFav; return ( , + href: pathForAdminPhotoEdit(photo.id), + }, { + label: isFav ? 'Unfavorite' : 'Favorite', + icon: , + action: () => toggleFavoritePhoto(photo.id, shouldRedirect), + }, + ], ...props, }}/> ); diff --git a/src/components/MoreMenu.tsx b/src/components/MoreMenu.tsx index d3d6aef4..8c41ab1a 100644 --- a/src/components/MoreMenu.tsx +++ b/src/components/MoreMenu.tsx @@ -2,17 +2,45 @@ import { clsx} from 'clsx/lite'; import Link from 'next/link'; import { Menu } from '@headlessui/react'; import { FiMoreHorizontal } from 'react-icons/fi'; -import { ReactNode } from 'react'; +import { Fragment, ReactNode, useState } from 'react'; export default function MoreMenu({ items, className, buttonClassName, }: { - items: { href: string, label: ReactNode }[] + items: { + label: ReactNode, + icon?: ReactNode, + href?: string, + action?: () => Promise, + }[] className?: string buttonClassName?: string }) { + const [isLoading, setIsLoading] = useState(false); + + const itemClass = clsx( + 'block w-full', + 'border-none min-h-0 bg-transparent', + 'text-left', + 'px-3 py-1.5 rounded-[3px]', + 'hover:text-main', + 'hover:bg-gray-50 active:bg-gray-100', + 'hover:dark:bg-gray-900/75 active:dark:bg-gray-900', + 'whitespace-nowrap', + isLoading && 'cursor-not-allowed opacity-50', + ); + + const renderItemContent = ( + label: ReactNode, + icon?: ReactNode, + ) => +
+ {icon} + {label} +
; + return (
- {items.map(({ href, label }) => - - - {label} - + {items.map(({ label, icon, href, action }) => + + <> + {href && + + {renderItemContent(label, icon)} + } + {action && + } + )} diff --git a/src/photo/PhotoLarge.tsx b/src/photo/PhotoLarge.tsx index a9cc1646..ecfe79ac 100644 --- a/src/photo/PhotoLarge.tsx +++ b/src/photo/PhotoLarge.tsx @@ -90,7 +90,7 @@ export default function PhotoLarge({
- +
diff --git a/src/photo/actions.ts b/src/photo/actions.ts index db8137ec..0f272b48 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -23,7 +23,7 @@ import { revalidateAllKeysAndPaths, revalidatePhotosKey, } from '@/photo/cache'; -import { PATH_ADMIN_PHOTOS, PATH_ADMIN_TAGS } from '@/site/paths'; +import { PATH_ADMIN_PHOTOS, PATH_ADMIN_TAGS, PATH_ROOT } from '@/site/paths'; import { extractExifDataFromBlobPath } from './server'; import { TAG_FAVS, isTagFavs } from '@/tag'; import { convertPhotoToPhotoDbInsert } from '.'; @@ -52,7 +52,10 @@ export async function updatePhotoAction(formData: FormData) { redirect(PATH_ADMIN_PHOTOS); } -export async function toggleFavoritePhoto(photoId: string) { +export async function toggleFavoritePhoto( + photoId: string, + shouldRedirect?: boolean, +) { const photo = await getPhoto(photoId); if (photo) { const { tags } = photo; @@ -61,6 +64,9 @@ export async function toggleFavoritePhoto(photoId: string) { : [...tags, TAG_FAVS]; await sqlUpdatePhoto(convertPhotoToPhotoDbInsert(photo)); revalidateAllKeysAndPaths(); + if (shouldRedirect) { + redirect(PATH_ROOT); + } } } diff --git a/src/tag/index.ts b/src/tag/index.ts index 58384d0a..60f9ed86 100644 --- a/src/tag/index.ts +++ b/src/tag/index.ts @@ -4,7 +4,11 @@ import { descriptionForPhotoSet, photoQuantityText, } from '@/photo'; -import { absolutePathForTag, absolutePathForTagImage } from '@/site/paths'; +import { + absolutePathForTag, + absolutePathForTagImage, + getPathComponents, +} from '@/site/paths'; import { capitalizeWords, convertStringToArray } from '@/utility/string'; export const TAG_FAVS = 'favs'; @@ -77,3 +81,8 @@ export const generateMetaForTag = ( }); export const isTagFavs = (tag: string) => tag.toLowerCase() === TAG_FAVS; + +export const isPhotoFav = ({ tags }: Photo) => tags.some(isTagFavs); + +export const isPathFavs = (pathname?: string) => + getPathComponents(pathname).tag === TAG_FAVS;