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

Feat: cross-page single-pod copy-paste in mouse-bound ctrl-v way #158

Merged
merged 6 commits into from
Dec 21, 2022

Conversation

li-xin-yi
Copy link
Collaborator

@li-xin-yi li-xin-yi commented Dec 20, 2022

[Done] This PR is finished, ready to review now.

(Only support one single code pod by now, Ctrl-C is not supported yet)

Related to: #155

I gave up clipboard.read/write, removed the paste option in right-click menu, tested on Chrome, Edges, and Firefox, all works well.

1671534645391

How to use:

  1. Copy a pod (either in this page or other pages) via the copy button
  2. Click (or right-click) on the canvas (pane) once to make sure the pane is focused
  3. Press Ctrl -V, a duplicated pod shows up in the center of the canvas (FIXME: because the cursor may not be in the canvas when pasting, so I just choose the center as the initial position, we can discuss the detailed rule for the initial position later)
  4. Move your mouse inside the canvas, the half-transparent pasted pod will move together, but the collaborator will never see this half-transparent pod. (To be exactly, the top-left corner position of this pod comes following the mouse, we may discuss the anchor point later, which point do you prefer? The center of the pod? Or the top-left?)
  5. Click on the destination position, then the pasted pod completely shows up, others can also see it on their pages.

Don't merge, just a demo to convey my idea and for test usages from others. The code is too dirty now, if you agree on this idea, I can continue to improve the implementation.

@li-xin-yi li-xin-yi marked this pull request as draft December 20, 2022 11:32
style,
};

addPod(apolloClient, {
Copy link
Collaborator

@lihebi lihebi Dec 20, 2022

Choose a reason for hiding this comment

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

In the future, we can defer the write to DB until the final drop of the node (when we allow a user to press Esc to cancel the paste). No need to implement that now because that seems to require non-trivial logic. Adding a TODO comment is good enough.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Also, since we called addPod in observer.YMapEvent.changes:

const observer = (YMapEvent) => {
YMapEvent.changes.keys.forEach((change, key) => {
if (change.action === "add") {
const node = nodesMap.get(key);
if (!node) return;
addPod(null, {

I think all addPod calls in Canvas.tsx are duplicate, right? Should we remove them? I.e. this line (Line 1170 in this PR) and the following line (Line 901 in main):

addPod(apolloClient, {

Copy link
Collaborator

Choose a reason for hiding this comment

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

addPod calls in Canvas.tsx are duplicate, right?

That depends on the behavior of ymap.observe. It is triggered if "other users" make the change. Would it get triggered as well if the "local user" makes the change?

Copy link
Collaborator Author

@li-xin-yi li-xin-yi Dec 20, 2022

Choose a reason for hiding this comment

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

In the future, we can defer the write to DB until the final drop of the node

In fact, when a user press the shortcut, the half-transparent node is immediately added to the pods array in local store, and nodesMap, but have not been synced to the DB (see the addpod arguments, I set the dirty false, doRemoteAdd will not be executed.

When the user determines the destination by clicking, I delete the temporary transparent node in nodesMap, and add a new formal node with everything identical but style.opacity and clientId removed, then write it formally to the DB.

ymap.observe on adding events here serves like the way in which we use useEffect to sync pod.name and pod.position before, only sync the data in nodesMap with local store.pods on each user's own side.

To avoid the duplicated pod adding in local store, I check if the pod is already in store.pods here: https://github.com/li-xin-yi/codepod-1/blob/experimental%2Fpaste-on-canvas/ui/src/lib/nodes.tsx#L114

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, my bad, I noticed that I set the dirty=true for the temporary pod adding just now. I will fix it.

Comment on lines +1187 to +1188
nodesMap.set(id, newNode as any);
setPasting(id);
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we set nodesMap, it would automatically sync with collaborators, right? UPDATE resolved; I saw that you checked clientID in various places in lib/nodes.tsx. That's a clever trick, looks good to me!

Copy link
Collaborator Author

@li-xin-yi li-xin-yi Dec 20, 2022

Choose a reason for hiding this comment

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

Yes, it is an adding event in nodesMap, usually, ymap.observe triggers a addPod call without remote DB writing, just adds it to the store.pods in each user's side. In this case, since I add the checking statement for clientId, collaborators will ignore it and won't call addPod until we delete the temporary floating transparent node and re-add the formal node.

node.position = position;
nodesMap.set(pasting, node);
};
const mouseClick = (event) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be helpful if the user could cancel the pasting before the drop by pressing Esc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree, sounds great, I will work on this feature later.

@lihebi
Copy link
Collaborator

lihebi commented Dec 20, 2022

  1. because the cursor may not be in the canvas when pasting, so I just choose the center as the initial position

LGTM

  1. the top-left corner position of this pod comes following the mouse

LGTM

@lihebi
Copy link
Collaborator

lihebi commented Dec 20, 2022

I gave up clipboard.read/write, removed the paste option in right-click menu, tested on Chrome, Edges, and Firefox, all works well.

LGTM. Let's forget about the right-click context menu for now.

@li-xin-yi
Copy link
Collaborator Author

Update:

  1. Fix the immediate adding to DB issue (see this comment)
  2. Add onClick listener to tooltip of codeNode to set the pane focus, then you can paste right after clicking on the copy button without additional clicking on the pane.
  3. Press Esc to cancel the in-progress pasting.
  4. If the destination is located inside a scope, drop this codeNode into the scope like what we did in onNodeDragStop.
  5. If there is already data on the yjs server for this code editor, reversely initialize pod.content to current code on when mounting the Monaco editor. Because collaborators pulled new node info from nodesMap, in which nodes don't have the information of code content, the store.pods[id].content will be initialized as null and kept until the code changes. This is a horrible data inconsistent issue between front-end code display <-> store.pods[id].content <-> pods[id].content in DB, for example, if you copy and paste a pod in canvas, everything looks normal, but actually the new pasted pod has an empty pod.content field, if you copy this new pod and then paste, you can see nothing on the new pasted pod.

Code still needs cleaning up and performance improvement.

@li-xin-yi
Copy link
Collaborator Author

li-xin-yi commented Dec 21, 2022

Update:

  • The reverse initialization of a temporary pod makes the pod dirty, but it won't have a truth source in DB until determined, so the cloud uploading must fail with error. Fix it by initialize the content without setting the pod dirty.

To-do:

  1. clean up the code
  2. improve the performance
  3. after pressing esc, a black inner block shows up in the pane, indicating the whole pane is selected. The event is triggered by esc by default (see this source code, the enter and space have the same effect on pane). I have to figure out a solution to prevent or un-select it.

@lihebi
Copy link
Collaborator

lihebi commented Dec 21, 2022

Thanks, all look and work great!

@li-xin-yi
Copy link
Collaborator Author

Updata:

Clean up the code

  1. Disable duplicated pasting when a pasting is not finished.
  2. Fix the tab selection black border issue there
  3. Unselect all pods when a pasting starts, make sure the processing pods always in the front-most, never shadowed by others.

@li-xin-yi li-xin-yi marked this pull request as ready for review December 21, 2022 19:10
@li-xin-yi li-xin-yi changed the title [WIP] A demo to show the pasted pod following the mouse Feat: cross-page single-pod copy-paste in mouse-bound ctrl-v way Dec 21, 2022
Copy link
Collaborator

@lihebi lihebi left a comment

Choose a reason for hiding this comment

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

Awesome, thanks!

@lihebi lihebi merged commit efc7e7c into codepod-io:main Dec 21, 2022
@lihebi lihebi linked an issue Dec 21, 2022 that may be closed by this pull request
4 tasks
@@ -907,6 +957,7 @@ export function Canvas() {
y: position.y,
width: style.width,
height: style.height,
dirty: true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need dirty: true here? It will cause a updatePod request, which might reach server before addPod, causing errors.

Screenshot 2023-01-06 at 11 28 26 AM

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The dirty parameter is removed in store.addPod but somehow remains here. It's a bug but shouldn't have any effect on addPod. Can you show me how to reproduce the error?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It does not reproduce on localhost. If you go to app.codepod.io, creating pods would sometime fail to sync.

But never mind, I've fixed it.

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.

Position to paste cells in ctrl-v way
2 participants