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

Get latest changes as a document instead of a binary #302

Open
RigoTamas opened this issue Mar 1, 2024 · 5 comments
Open

Get latest changes as a document instead of a binary #302

RigoTamas opened this issue Mar 1, 2024 · 5 comments

Comments

@RigoTamas
Copy link

I'm trying to get the changes between the previous state and the current state in a document, but I'm unable to do so. The patchInfo object contains the document before the changes and the document after the changes have been applied, but I'm not able to get a document that contains just the changes.

This would be important for me as I'd like to render only the changes on the UI. I cannot use a declarative approach for rendering, only an imperative one, therefore I need to know only what's new compared to the previous state.

I've tried the following approach, but it did not work:

import * as A from '@automerge/automerge';

documentHandle = repo.find(AUTOMERGE_URL);
documentHandle.on('change', (document) => {
    const changes = A.next.getChanges(document.patchInfo.before, document.patchInfo.after); // changes is an array of binary data
    const newHandle = repo.import(changes[0]); // throws RangeError: change's deps should already be in the document

    const emptyDocument = A.next.init<{ data: any[] }>();
    const clonedDoc = A.next.clone(emptyDocument);
    const loadedChanges = A.next.applyChanges(clonedDoc, changes); // loadedChanges contains nothing, it's an empty document
}

Is this even possible? If so how could I achieve it?

@acurrieclark
Copy link
Collaborator

You won't be able to get the changes as an object from the change event callback, but anything which has been altered in a specific change is contained within the patches array:

const documentHandle = repo.find(AUTOMERGE_URL);
documentHandle.on('change', ({doc, patches}) => {
  console.log(doc, patches);
}

See Patch reference in the API docs

@RigoTamas
Copy link
Author

Is there an easy way to tally up these patches to get a single JS object that represents the changes between the previous and the current state?

@acurrieclark
Copy link
Collaborator

You could take the patches and walk the before and after documents to create a new document with any changed properties?

Can you give us a more concrete use case. There might be a better option here.

@RigoTamas
Copy link
Author

My use case is currently very simple. The content that I'm sharing between peers is an array that contains the relevant data. Currently users can only create new data, and that data is pushed to the array. Therefore it's pretty simple to write code that gets the data that is new:

documentHandle.on('change', (document) => {
    const dataToRender = document.patchInfo.after?.data?.slice(document.patchInfo.before?.data?.length) || [];
    renderData(dataToRender)
});

However it could get more complicated. I might want to delete arbitrary data from the array, or even change the data structure completely, to not have a single array but an object that can hold multiple arrays.

That's why I was seeking a general solution to get only the difference between the new and the old document.

@pvh
Copy link
Member

pvh commented Mar 1, 2024

Ah! What you want is a patch, not a change. (I realize this is confusing, sorry.) Changes are compact internal binary representations of data for shuttling around over the network and storing on disk. They represent a bundle of operations created by a single users' call to change() (or whatever local-language equivalent.)

Patches are very similar to a JSONPatch data structure and describe exactly what you want -- the precise difference between one set of heads and another. They're used internally to maintain the materialized objects but we also use them in user-land to handle incremental updates of things like {Prose, Code}Mirror and TLDraw.

You get a patch in the on("change" () =>{}) callback on a DocHandle, but you also can create a patch between any two arbitrary sets of Heads with Automerge.diff().

Have a look at that and see if it gets you on a good path.

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

No branches or pull requests

3 participants