Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added onSuccessAction prop to Upload Widget #487

Merged
merged 6 commits into from
Jul 30, 2024

Conversation

JoshuaRotimi
Copy link
Contributor

@JoshuaRotimi JoshuaRotimi commented Jul 4, 2024

Description

This PR adds an onSuccessAction prop to the CloudUploadWidget component.

The feature makes a few changes to the CloudUploadWidget and CloudUploadButton components by adding a new onSuccessAction prop where a server action can be passed as a function to be called on the success of an image upload.

Issue Ticket Number

Fixes #486

Fixes: #486

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Fix or improve the documentation
  • [ . ] This change requires a documentation update

Copy link

vercel bot commented Jul 4, 2024

@JoshuaRotimi is attempting to deploy a commit to the Cloudinary DevX Team on Vercel.

A member of the Team first needs to authorize it.

@colbyfayock
Copy link
Collaborator

colbyfayock commented Jul 4, 2024

hey @JoshuaRotimi thanks for putting this together. have a few requests:

  • can you add a little more context and usage details to the PR for historical and release notes?

  • i tried using this with CldUploadButton and realized that it wasn't set up to pass the props through, can you set this up so that CldUploadButton can use it as well?

image
  • the FormData got me thinking, is ther eanything wrong wiht passing JSON to a Server Action? i tested passing the upload result directly to the onSuccessAction and it seemed to "just work", that would be an easier way to interface with it, though my original assumption was FormData was the intended format for Server Actions

  • can you add an example of this working inside of src/tests/nextjs-app so that it runs and can easily be tested?

  • i think part of my hope for this was that we could automate the action creation for each callback type. in the code for intsance, the way that the callback function currently is determined is by:

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

if ( typeof widgetEvent === 'string' && typeof props[widgetEvent] === 'function' ) {
  const callback = props[widgetEvent] as CldUploadEventCallback;
  callback(uploadResult, {
    widget: widget.current,
    ...instanceMethods
  });
}

it's not unreasonable to think that we could add onto that by doing osmething like:

const widgetEventAction = typeof widgetEvent === 'string' && `${widgetEvent}Action`;

if ( widgetEventAction && typeof props[widgetEventAction] === 'function' ) {
  const action = props[widgetEventAction] as CldUploadEventAction;
  onSuccessAction(uploadResult);
}

wdyt?

@JoshuaRotimi
Copy link
Contributor Author

Hello @colbyfayock

Okay, I will add more information about the feature to the PR.

About the JSON/FormData, I think we could switch to JSON, it makes the code more readable without the Object.entries loop, I thought you may have had a definite reason why FormData was the best option.

If we want to automate the action creation the way you stated, we would have to add types for all the other instances like onDisplayChangedAction, onUploadAddedAction and the rest of the CloudWidgetProps actions. is that what we want to do?

Okay, I can add the same functionality to the CloudUploadButton once we have it working correctly for the CloudUploadWidget.

@colbyfayock
Copy link
Collaborator

The reason was really just because ive only ever really seen FormData examples, so i was thinking it was the standard, but looking into it more it appears that you can pass anything, and didnt see any objections when i asked around about it on twitter (tho not many responses)

i think adding the types for each one makes sense, we're already doing that for the standard ones, wish there was a way to automate that as well. i think it would make the implementation more complete and more useful for people

thanks Josh! and apologies for the delay, currently traveling

@JoshuaRotimi
Copy link
Contributor Author

Yeah I figured you've been busy as I also did not receive last week's newsletter in my email haha. Hope your trip is going good?

Okay that sounds great. I've made some updates to the code as regards to what we spoke about. Please let me know if everything looks great and then I can implement the same for the CloudUploadButton component. Thanks

@@ -57,6 +58,8 @@ const CldUploadWidget = ({

const signed = !!signatureEndpoint;

const { onSuccessAction } = props;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont think this is needed anymore


if ( widgetEventAction && typeof props[widgetEventAction] === 'function' ) {
const action = props[widgetEventAction] as CldUploadEventAction;
uploadResult.event === 'success' && action(uploadResult);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can remove the 'success' check here right? as long as the function exists and its under the watched events, which at this point i believe both should be true, we should be good, right?

@@ -34,6 +34,20 @@ export interface CldUploadWidgetProps {
options?: CloudinaryUploadWidgetOptions;
signatureEndpoint?: URL | RequestInfo;
uploadPreset?: string;
onSuccessAction?: CldUploadEventAction;
onUploadAction?: CldUploadEventAction;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the onUpload function is deprecated and will be removed in a later version (likely a major?), so thinking we probably shouldnt add this as an option. while it will technically work if someone tries because the actions are automatically created, we don't want to document or promote its use as a valid option only for it to be pulled out soon

@colbyfayock
Copy link
Collaborator

colbyfayock commented Jul 25, 2024 via email

@JoshuaRotimi
Copy link
Contributor Author

Okay sounds great. I'll make the changes and push again. For the docs update, do we want to add all the new methods like onSuccessAction, onDisplayChangedAction to the events part of the docs? For the other functions, clicking the "More Info" link takes you to the Cloudinary API for the function. What do we want to display for these methods?

@colbyfayock
Copy link
Collaborator

yeah i think under Events makes sense, and that could either be in the same group as Functions or a separate Actions, i dont have a strong opinion there, but maybe a separate Actions section under Events makes sense

im thinking about the differentiator between Actions and Functions here. originally the biggest differentiator was the FormData, but now we're passing the same javascript object as the first argument, but not passing the 2nd argument of the options. we can't pass that 2nd argument because it has javascript instances in there, but mainly, its the same except fro that second argument

any thoughts any anything else we can use to differentiate the two aside from that? for instance, if someone is working clientside, why would they want to use an action vs a function? should someone always use a Function unless they are using Actions, which then its the same but limited functionality? (no options argument)

mostly thinking out loud, thats probably fine, but wondering if we have an opportunity to provide any additional useful information or capabilities as part of the Actions workflow

@colbyfayock
Copy link
Collaborator

as far as the More Info link, i think we can link to the same spot, as its primarily at this time meant to link to the information about the event that triggers it

@colbyfayock
Copy link
Collaborator

at a minimum, at the top of the Events section, we can simply state the difference at this time is that the Action doesn't pass in the options argument that includes the widget and instance methods to allow it to function as an Action

@JoshuaRotimi
Copy link
Contributor Author

Yes the difference is in the arguments from both functions. Okay that makes sense. I will add that to the docs.

One quick question, can we add a function like this to avoid repetitiveness in the CloudUploadButton component or we should just follow the current pattern.

  const actionProps = Object.keys(props)
      .filter(key => key.endsWith('Action'))
      .reduce((result, key) => {
        //@ts-ignore
        result[key] = props[key];
        return result;
      }, {});

and then in the return statement, we can just spread the actionProps object.

@colbyfayock
Copy link
Collaborator

honestly i can't think of a good reason why we don't pass in the entire props object and utilize the UploadWidget and the types to maintain the props that are useful

it's no different than creating a <Button where you spread the rest of the props on a <button {...props to allow someone to configure it as theyd like, so likely you can remove all of the redundantly defined props in that component, do you agree?

@JoshuaRotimi
Copy link
Contributor Author

Okay that's fair. Maybe we can create that as a separate issue? Refactoring now may cause some typescript problems.

@colbyfayock
Copy link
Collaborator

colbyfayock commented Jul 26, 2024 via email

@JoshuaRotimi
Copy link
Contributor Author

Made the changes and pushed the code.

Copy link

vercel bot commented Jul 29, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
next-cloudinary ✅ Ready (Inspect) Visit Preview Jul 30, 2024 0:54am

@@ -33,13 +33,21 @@ const CldUploadButton = ({
...props
}: CldUploadButtonProps) => {

const actionProps = Object.keys(props)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we can just pass through all of the props at this point and avoid filtering them down here

this will allow the Button to always inherit any available options on the UploadWidget without having to add it in both places, unless you're doing this because of the TS issue you mentioned before

@colbyfayock
Copy link
Collaborator

colbyfayock commented Jul 29, 2024

just noticed an issue when testing htis out locally.

it looks like as it currently stands, the rest of the ...props on the CldUploadButton are being passed through to the <button itself

Because the action props are not being destructured from that props object, the event handler is getting applied to the <button and creating a warning

image

this makes me unsure of whether we should pass through the props as i mentioned before directly to the uploadWidget or not. i lean towards yes and having a different mechanism for passing through the <button props, but this would be a breaking change so we should save that as another Issue that gets grouped in with the next major release #441

so for now, i believe we may need to, unfortunately, destructure all of the Action props similar to what we're doing with the callback function props, unless you have a different idea

that said, i tested it out and its working, so hooray! 🙌

image

@JoshuaRotimi
Copy link
Contributor Author

just noticed an issue when testing htis out locally.

it looks like as it currently stands, the rest of the ...props on the CldUploadButton are being passed through to the <button itself

Because the action props are not being destructured from that props object, the event handler is getting applied to the <button and creating a warning

image this makes me unsure of whether we should pass through the `props` as i mentioned before directly to the `uploadWidget` or not. i lean towards yes and having a different mechanism for passing through the `so for now, i believe we may need to, unfortunately, destructure all of the Action props similar to what we're doing with the callback function props, unless you have a different idea

that said, i tested it out and its working, so hooray! 🙌

image

Ah I see what you mean, the reason we have that warning is because the button reads only native HTML button attributes like onKeyDown, onPress and the likes. We don't see that error because the ...props object is currently empty as we are spreading everything out on the CldUploadButton. So yes you are right, that may be the best approach for now. I was only using the actionProps function to try to reduce repetition. I will do that now and make another push.

@colbyfayock
Copy link
Collaborator

this is working great now!! thanks @JoshuaRotimi for the PR and your patience as we worked through it

@colbyfayock colbyfayock merged commit ea29cf3 into cloudinary-community:main Jul 30, 2024
4 checks passed
github-actions bot pushed a commit that referenced this pull request Jul 30, 2024
# [6.7.0](v6.6.2...v6.7.0) (2024-07-30)

### Features

* Action Props for CldUploadWidget ([#487](#487)) ([ea29cf3](ea29cf3)), closes [#486](#486)
Copy link

🎉 This PR is included in version 6.7.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@JoshuaRotimi
Copy link
Contributor Author

Awesome. Always happy to contribute!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature] CldUploadWidget - Callbacks as Actions
2 participants