From 43cdb4b8fec4ffbb028ab6d3d8ca1690e50a028f Mon Sep 17 00:00:00 2001 From: samuraikun Date: Sat, 17 Feb 2024 17:06:10 -0800 Subject: [PATCH] create AcitivityUploadedFile record when upload activity image. --- app/activity/[id]/edit/page.tsx | 11 ++--- app/activity/[id]/page.tsx | 15 ++----- app/activity/components/activity-form.tsx | 51 ++++++++++++++++------- app/activity/hooks/useUploadFiles.ts | 33 +++++++++++---- app/providers/session-provider.tsx | 1 - package.json | 1 - pnpm-lock.yaml | 27 ------------ 7 files changed, 69 insertions(+), 70 deletions(-) diff --git a/app/activity/[id]/edit/page.tsx b/app/activity/[id]/edit/page.tsx index b8ee3b7..87dbd5c 100644 --- a/app/activity/[id]/edit/page.tsx +++ b/app/activity/[id]/edit/page.tsx @@ -27,12 +27,6 @@ export default function ActivityEditPage({ notifyOnNetworkStatusChange: true }) - const dummyUrls: string[] = [ - 'https://images.unsplash.com/photo-1612852098516-55d01c75769a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDR8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60', - 'https://images.unsplash.com/photo-1627875764093-315831ac12f7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60', - 'https://images.unsplash.com/photo-1571432248690-7fd6980a1ae2?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDl8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60' - ] - const activityDataCollection = activityData?.activityCollection?.edges[0]?.node const activityDetailsRefetch = () => { @@ -68,7 +62,10 @@ export default function ActivityEditPage({ memo: activityDataCollection?.memo, cost: activityDataCollection?.cost, costUnit: activityDataCollection?.cost_unit, - uploadedFileUrls: dummyUrls, + uploadedFileUrls: + activityDataCollection?.activity_uploaded_filesCollection?.edges.map( + (edge) => edge?.node?.file_url + ), refetch: activityDetailsRefetch, refetchLoading: activityDetailsRefetchLoading }} diff --git a/app/activity/[id]/page.tsx b/app/activity/[id]/page.tsx index 4102db6..c0ef88e 100644 --- a/app/activity/[id]/page.tsx +++ b/app/activity/[id]/page.tsx @@ -21,18 +21,12 @@ export default function ActivityDetails({ } }) - const dummyUrls: string[] = [ - 'https://images.unsplash.com/photo-1612852098516-55d01c75769a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDR8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60', - 'https://images.unsplash.com/photo-1627875764093-315831ac12f7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60', - 'https://images.unsplash.com/photo-1571432248690-7fd6980a1ae2?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDl8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60' - ] - if (!loading && !data) throw new Error('No activity data found') const activityData = data?.activityCollection?.edges[0]?.node const uploadedImageUrls = activityData?.activity_uploaded_filesCollection ? activityData?.activity_uploaded_filesCollection?.edges.map( - (edge) => edge?.node?.file_url || '' + (edge) => edge?.node?.file_url ) : [] @@ -56,10 +50,9 @@ export default function ActivityDetails({ address={activityData?.address} url={activityData?.url} /> - {/* TODO: Fetch images from the database once available. */} - 0 ? uploadedImageUrls : dummyUrls} - /> + {uploadedImageUrls.length > 0 && ( + + )} diff --git a/app/activity/components/activity-form.tsx b/app/activity/components/activity-form.tsx index 80e2fd3..7ac3807 100644 --- a/app/activity/components/activity-form.tsx +++ b/app/activity/components/activity-form.tsx @@ -1,6 +1,5 @@ -import React, { useCallback, useState } from 'react' -import { useDropzone } from 'react-dropzone' -import { Controller, useForm } from 'react-hook-form' +import React, { useState } from 'react' +import { Controller, useForm, useWatch } from 'react-hook-form' import { Box, FormControl, @@ -11,7 +10,8 @@ import { SimpleGrid, Text, Flex, - VStack + VStack, + Input } from '@chakra-ui/react' import { PrimaryButton } from '@/components/button' import { CustomDateTimePicker } from '@/components/customDateTimePicker' @@ -45,11 +45,7 @@ export const ActivityForm = ({ activityDetails }: ActivityFormProps) => { const [selectedImages, setSelectedImages] = useState([]) - const onDrop = useCallback((acceptedFiles: File[]) => { - setSelectedImages((prevImages) => [...prevImages, ...acceptedFiles]) - }, []) - const { getRootProps, getInputProps } = useDropzone({ onDrop }) - function removeImage(index: number) { + const removeImage = (index: number) => { setSelectedImages((prevImages) => { const newImages = [...prevImages] newImages.splice(index, 1) @@ -77,10 +73,11 @@ export const ActivityForm = ({ cost: activityDetails?.cost || undefined, costUnit: activityDetails?.costUnit || undefined, uploadedFileUrls: activityDetails?.uploadedFileUrls || undefined, - newFiles: selectedImages + newFiles: [] }, resolver: activityResolver }) + const newFiles = useWatch({ control, name: 'newFiles' }) const { createActivity, isActivityCreating } = useActivityCreate(tripId) const { updateActivity, isActivityUpdating } = useActivityUpdate(tripId) @@ -180,6 +177,7 @@ export const ActivityForm = ({ width={{ base: '160px', md: '180px' }} height={{ base: '106px', md: '120px' }} margin="auto" + _hover={{ cursor: 'pointer' }} /> ))} {selectedImages.map((image, index) => ( @@ -191,13 +189,13 @@ export const ActivityForm = ({ width={{ base: '160px', md: '180px' }} height={{ base: '106px', md: '120px' }} margin="auto" + _hover={{ cursor: 'pointer' }} onClick={() => removeImage(index)} /> ))} - - - Add Image - + ( + + Add Image + { + const files = event.target.files + if (files && newFiles) { + const newFileList = Array.from(files) + setSelectedImages((prevImages) => [ + ...prevImages, + ...newFileList + ]) + newFiles.push(...newFileList) + onChange(newFiles) + } + }} + /> + + )} + /> diff --git a/app/activity/hooks/useUploadFiles.ts b/app/activity/hooks/useUploadFiles.ts index 8e8c245..c3f277e 100644 --- a/app/activity/hooks/useUploadFiles.ts +++ b/app/activity/hooks/useUploadFiles.ts @@ -1,7 +1,9 @@ import { createClient } from '@supabase/supabase-js' -// import { useCreateActivityUploadedFilesMutation } from '@generated/api' +import { useCreateActivityUploadedFilesMutation } from '@generated/api' export const useUploadFiles = () => { + const [createActivityUploadedFilesMutation] = useCreateActivityUploadedFilesMutation() + const uploadFiles = async ( files: File[], activityDetails: { id: string; tripId: string } @@ -13,7 +15,7 @@ export const useUploadFiles = () => { const uploadPromises = files.map(async (file) => { const { data, error } = await supabase.storage - .from('tabi-memo-uploads') + .from(process.env.NEXT_PUBLIC_BUCKET_NAME!) .upload( `trips/${activityDetails.tripId}/activity/${activityDetails.id}/${file.name}`, file, @@ -24,18 +26,33 @@ export const useUploadFiles = () => { }) const uploadResults = await Promise.all(uploadPromises) - const uploadErrors = uploadResults.filter((result) => result.error) - if (uploadErrors.length) { + if (uploadErrors.length > 0) { throw new Error(uploadErrors[0].error!.message) } + // get the public URL of the uploaded files + const results = uploadResults.map((result) => result) + const dataWithPublicUrls = await Promise.all( + results.map((result) => { + const { data: { publicUrl } } = supabase.storage.from(process.env.NEXT_PUBLIC_BUCKET_NAME!).getPublicUrl(result.data!.path!) + + return { publicUrl, fileName: result.fileName } + }) + ) + // create a record in the uploaded_files table - // const uploadedFiles = uploadResults.map((result) => ({ - // file_name: result.fileName, - // file_url: result.data?.path - // })) + await createActivityUploadedFilesMutation({ + variables: { + objects: dataWithPublicUrls.map((data) => ({ + activity_id: activityDetails.id, + file_name: data.fileName, + file_url: data.publicUrl, + })) + }, + refetchQueries: ['activityCollection'] + }) } return { uploadFiles } diff --git a/app/providers/session-provider.tsx b/app/providers/session-provider.tsx index ccc26d2..17a93d0 100644 --- a/app/providers/session-provider.tsx +++ b/app/providers/session-provider.tsx @@ -21,7 +21,6 @@ export function SessionProvider({ userId: string }) { const pathname = usePathname() - console.log(pathname) const AUTH_PATH_NAMES = ['/signin', '/signup'] return AUTH_PATH_NAMES.includes(pathname) ? ( {children} diff --git a/package.json b/package.json index 24a5dfe..643ecb1 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "react-datepicker": "^5.0.0", "react-datetime-picker": "^5.6.0", "react-dom": "^18", - "react-dropzone": "^14.2.3", "react-slick": "^0.29.0", "slick-carousel": "^1.8.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6dca88..e643e88 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,9 +62,6 @@ dependencies: react-dom: specifier: ^18 version: 18.2.0(react@18.2.0) - react-dropzone: - specifier: ^14.2.3 - version: 14.2.3(react@18.2.0) react-slick: specifier: ^0.29.0 version: 0.29.0(react-dom@18.2.0)(react@18.2.0) @@ -8436,11 +8433,6 @@ packages: hasBin: true dev: true - /attr-accept@2.2.2: - resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==} - engines: {node: '>=4'} - dev: false - /auto-bind@4.0.0: resolution: {integrity: sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==} engines: {node: '>=8'} @@ -11140,13 +11132,6 @@ packages: flat-cache: 3.1.1 dev: true - /file-selector@0.6.0: - resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==} - engines: {node: '>= 12'} - dependencies: - tslib: 2.6.2 - dev: false - /file-system-cache@1.1.0: resolution: {integrity: sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw==} dependencies: @@ -15243,18 +15228,6 @@ packages: react: 18.2.0 scheduler: 0.23.0 - /react-dropzone@14.2.3(react@18.2.0): - resolution: {integrity: sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==} - engines: {node: '>= 10.13'} - peerDependencies: - react: '>= 16.8 || 18.0.0' - dependencies: - attr-accept: 2.2.2 - file-selector: 0.6.0 - prop-types: 15.8.1 - react: 18.2.0 - dev: false - /react-element-to-jsx-string@15.0.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==} peerDependencies: