cloudinary-community / next-cloudinary

⚡️ High-performance image delivery and uploading at scale in Next.js powered by Cloudinary.
https://next.cloudinary.dev
MIT License
237 stars 64 forks source link

[Feature] CldUploadWidget - Callbacks as Actions #486

Open colbyfayock opened 1 month ago

colbyfayock commented 1 month ago

Feature Request

Is your feature request related to a problem? Please describe.

<CldUploadWidget onSuccessAction={successAction}

Would allow for easier interaction between the server and client components

JoshuaRotimi commented 3 weeks ago

@colbyfayock could you share more information about how this should work? Also, does onSuccessAction replace the onUpload method ?

colbyfayock commented 3 weeks ago

@JoshuaRotimi apologies for the short Issue, i was planning on handling this but haven't gotten around to it

when playing with this before, the way i had imagined this working is that there's a new prop onSuccessAction (separately maintained from onSuccess (onUpload was deprecated)

this prop would take a Server Action, that would be treated as a callback, similar to onSuccess

for instance:

// actions.ts

async function storeImage(formData: FormData) {
  // Grab resource and save references in a database
}

// mycomponent.tsx

<CldUploadWidget onSuccessAction={storeImage} />

Inside of the CldUploadWidget component, we would need to invoke the action like an async function:

// CldUploadWidget.tsx

const { onSuccessAction } = props;

const formData = new FormData();

formData.append('resource', upload);

await onSuccessAction(formData);

This would give more flexibilty to use the upload widget from a server component

Considerations:

const widgetEvent = WIDGET_EVENTS[uploadResult.event] as keyof typeof props;

if ( props[widgetEvent] === 'function' )
if ( props[`${widgetEvent}Action`] === 'function' )
formData.append('upload', JSON.stringify(uploadResult))

// vs

Object.entries(uploadResult).forEach(([key, value]) => {
  formData.append(key, JSON.stringify(value))
})

I lean towards the latter to avoid one huge string but i'm open to suggestions and ideas around this. perhaps we can see if there's precedent for how others have handled this

what do you thikn?

JoshuaRotimi commented 3 weeks ago

Okay, thank you for the clarification. I think I have an understanding of what you mean now. A few questions:

  1. would we create an async function inside the createWidget function to handle the onSuccessAction function or make the createWidget function itself an async function?
  2. does this mean the component would accept and use either the onSuccess OR the onSuccessAction prop and not both? I think the Object.entries approach might be the better option
colbyfayock commented 3 weeks ago
  1. the onSuccessAction is the async function. i'm thinking that we don't necessarily need to await the onSuccessAction callback inside of createWidget as we're not depending on it, os it can fire off asynchronously as the callback, similar to the original one. onSuccessAction being async shouldnt change things (and technically might not need to be async itself?)

  2. it should accept both onSuccess AND onSuccessAction as they're both valid use cases, onSuccessAction being specific to server actions. they both have different argument signatures

JoshuaRotimi commented 3 weeks ago

Okay that makes sense. Let me give it a try, you can assign it to me.

colbyfayock commented 3 weeks ago

sounds great!