Skip to content

Conversation

@infomiho
Copy link
Contributor

@infomiho infomiho commented May 9, 2025

This PR implements running all the steps from the tutorial in the docs. We encode all the code changes into Git patches which we apply one by one.

We implement tooling for working with the patches when something breaks:

  • if there is a new step and a patch is missing - it prompts you to do the code change and it then creates a patch
  • if a patch fails, it prompts you to redo the code change and then it creates a new patch

There are two commands:

  • generate-app which handles creating and apply patches
  • edit-step which is used to edit a patch and it rebases the changes

Check out the README.md for more info on using the

Note for reviewers: please try it out and think of any process that might be useful to cover with the CLI

Closes #2814

Copy link
Member

@cprecioso cprecioso left a comment

Choose a reason for hiding this comment

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

I think that, for consistency, I'd also have write actions take in a patch.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jul 16, 2025

Deploying wasp-docs-on-main with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4d82a92
Status: ✅  Deploy successful!
Preview URL: https://f3461c7f.wasp-docs-on-main.pages.dev
Branch Preview URL: https://miho-tutorial-app-from-docs.wasp-docs-on-main.pages.dev

View logs

@infomiho infomiho changed the title Tutorial app from Markdown WIP Automatically generate tutorial app from Markdown Jul 16, 2025
@infomiho infomiho force-pushed the miho-tutorial-app-from-docs branch from a260193 to 1348c89 Compare July 18, 2025 09:12
@infomiho infomiho requested a review from cprecioso July 18, 2025 10:50
Copy link
Contributor

@FranjoMindek FranjoMindek left a comment

Choose a reason for hiding this comment

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

I didn't go too deep this time (only new changes), but I like the current state.

Comment on lines +65 to +87
const timeoutRef = useRef<NodeJS.Timeout | null>(null);

const handleCopy = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}

navigator.clipboard.writeText(textToCopy);
setCopied(true);

timeoutRef.current = setTimeout(() => {
setCopied(false);
timeoutRef.current = null;
}, 1500);
};

useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
Copy link
Member

@cprecioso cprecioso Nov 12, 2025

Choose a reason for hiding this comment

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

No need for the ref

Suggested change
const timeoutRef = useRef<NodeJS.Timeout | null>(null);
const handleCopy = () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
navigator.clipboard.writeText(textToCopy);
setCopied(true);
timeoutRef.current = setTimeout(() => {
setCopied(false);
timeoutRef.current = null;
}, 1500);
};
useEffect(() => {
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, []);
const handleCopy = () => {
navigator.clipboard.writeText(textToCopy);
setCopied(true);
};
useEffect(() => {
if (copied) {
const timeout = setTimeout(() => {
setCopied(false);
}, 1500);
return () => {
clearTimeout(timeout);
};
}
}, [copied]);

Copy link
Contributor

Choose a reason for hiding this comment

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

While I think your solution is more elegant, I think the useRef solution is more correct.

The fact that the whole useEffect is inside one if statement is a bit of "code smell" for me.
We are triggering it even when we don't really want to.

React team heavily advises to handle user events inside of event handler functions rather than useEffect.
https://react.dev/learn/you-might-not-need-an-effect

Copy link
Member

Choose a reason for hiding this comment

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

I am aware, but the timer is not a user event (only the initial handleCopy is)

And they say:

You do need Effects to synchronize with external systems.

Time is an external system 😛

They also do put setTimeout as their example for an Effect in their documentation https://react.dev/learn/synchronizing-with-effects#putting-it-all-together

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice stuff @cprecioso

I really need to brush up my React knowledge, I'll implement the simpler solution 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think your solution covers the case of repeat clicks, but I'm willing to sacrifice if we don't have to use refs.

Copy link
Contributor

@FranjoMindek FranjoMindek Nov 12, 2025

Choose a reason for hiding this comment

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

You do need Effects to synchronize with external systems.

Time is an external system 😛

They also do put setTimeout as their example for an Effect in their documentation react.dev/learn/synchronizing-with-effects#putting-it-all-together

Yes, but to me the intention is different.
In our case the timer is directly tied to the logic of clicking on the copy element - the handling of the event.
We are handling an event which requires a timer to work.
We are not synchronizing an action with some event.

Copy link
Contributor

@FranjoMindek FranjoMindek Nov 12, 2025

Choose a reason for hiding this comment

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

If we want to do React examples, I think the stopwatch example is much closer to our problem, an example they have on their useRef page:

image

https://react.dev/reference/react/useRef#a-stopwatch

Copy link
Member

Choose a reason for hiding this comment

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

damn franjo got hands 🖐️

Copy link
Member

Choose a reason for hiding this comment

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

mmm okay, you convinced me! makes sense

Copy link
Member

Choose a reason for hiding this comment

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

This was impressive to read, I also got to upadte my React konwledge which is still stuck somewhere in 2020 :D.

Copy link
Member

@Martinsos Martinsos left a comment

Choose a reason for hiding this comment

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

Approving! But check the comments, there is still a couple of things to consider, before you merge. Step vs action, although yeah we can do it in another PR if we decide to do it, and something else.

I have to admit, I haven't done the final read of PR code, because there is quite a bit. Also, I never checked the git logic thoroughly, because I find it quite tedious to read. I think that is the indicator that code could be better, but I also think it might be good enough to pass, taking all into account, so I am ok with it. But it might be a good thing for future, how could have this been made easier to review? Probably by splitting git logic into separate PR, and by designing it somewhat different way, so it is easier to read / follow. We don't have to do that now though.

Ok, e2e tests are in the other PR, I will check them there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automatically test the tutorial app

5 participants