Skip to content

Commit

Permalink
New type created observer (#19)
Browse files Browse the repository at this point in the history
* wip: use new type created observer

* fix makeYDocObservable
  • Loading branch information
YousefED committed Oct 12, 2021
1 parent 6312530 commit f04255c
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 53 deletions.
2 changes: 1 addition & 1 deletion examples/todo-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"typescript": "^4.1.2",
"web-vitals": "^1.0.1",
"y-webrtc": "^10.1.8",
"yjs": "npm:type-created-yjs@13.5.11"
"yjs": "^13.5.13"
},
"scripts": {
"start": "react-scripts start",
Expand Down
2 changes: 1 addition & 1 deletion examples/todo-vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"core-js": "^3.6.5",
"vue": "^3.0.0",
"y-webrtc": "^10.1.8",
"yjs": "npm:type-created-yjs@13.5.11"
"yjs": "^13.5.13"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.18.0",
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions packages/reactive-crdt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@
"dependencies": {
"@reactivedata/reactive": "0.1.13",
"@reactivedata/yjs-reactive-bindings": "^0.1.8",
"@types/eslint": "6.8.0",
"yjs": "npm:type-created-yjs@13.5.11"
"@types/eslint": "6.8.0"
},
"peerDependencies": {
"yjs": "^13.5.13"
},
"devDependencies": {
"microbundle": "^0.13.0",
Expand Down
5 changes: 3 additions & 2 deletions packages/reactive-crdt/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as reactive from "@reactivedata/reactive";
import { makeYJSObservable, useReactiveBindings } from "@reactivedata/yjs-reactive-bindings";
import { makeYDocObservable, useReactiveBindings } from "@reactivedata/yjs-reactive-bindings";
import * as Y from "yjs";
import { CRDTArray, crdtArray } from "./array";
import { CRDTObject, crdtObject } from "./object";
Expand All @@ -8,7 +8,7 @@ import { JSONValue } from "./types";
export { useMobxBindings, useVueBindings } from "@reactivedata/yjs-reactive-bindings";

// setup yjs-reactive-bindings
makeYJSObservable();

useReactiveBindings(reactive); // use reactive bindings by default

export const INTERNAL_SYMBOL = Symbol("INTERNAL_SYMBOL");
Expand Down Expand Up @@ -60,6 +60,7 @@ export function crdtValue<T extends NestedSchemaType>(value: T | Y.Array<any> |
}

export function crdt<T extends ObjectSchemaType>(doc: Y.Doc) {
makeYDocObservable(doc);
return crdtObject({} as T, doc.getMap());
}

Expand Down
6 changes: 4 additions & 2 deletions packages/yjs-reactive-bindings/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
"types": "types/index.d.ts",
"license": "MIT",
"dependencies": {
"@types/eslint": "6.8.0",
"yjs": "npm:type-created-yjs@13.5.11"
"@types/eslint": "6.8.0"
},
"peerDependencies": {
"yjs": "^13.5.13"
},
"devDependencies": {
"microbundle": "^0.13.0",
Expand Down
34 changes: 31 additions & 3 deletions packages/yjs-reactive-bindings/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,38 @@ export function observeYJS(element: Y.AbstractType<any> | Y.Doc) {
return element;
}

export function makeYJSObservable() {
Y.observeTypeCreated(el => {
observeYJS(el);
function makeYDocRootLevelTypesObservable(doc: Y.Doc) {
doc.share.forEach(type => {
// the explicit check is necessary because we sometimes initialize "anonymous" types that the user can't (and shouldn't) access.
if (type.constructor !== Y.AbstractType) {
observeYJS(type);
}
});
}

export function makeYDocObservable(doc: Y.Doc) {
// based on https://github.com/yjs/yjs/pull/298#issuecomment-937636849
doc.on("beforeObserverCalls", tr => {
makeYDocRootLevelTypesObservable(doc);
tr.afterState.forEach((clock, client) => {
const beforeClock = tr.beforeState.get(client) || 0;
if (beforeClock !== clock) {
const structs = /** @type {Array<GC|Item>} */ tr.doc.store.clients.get(client);
const firstChangePos = Y.findIndexSS(structs, beforeClock);
for (let i = structs.length - 1; i >= firstChangePos; i--) {
if (!structs[i].deleted) {
structs[i].content?.getContent().forEach(content => {
if (content instanceof Y.AbstractType) {
observeYJS(content);
// console.log(content, "is a created type type");
}
});
}
}
}
});
});
makeYDocRootLevelTypesObservable(doc);
}

export { useMobxBindings, useReactiveBindings, useVueBindings } from "./observableProvider";
Expand Down
2 changes: 1 addition & 1 deletion packages/yjs-reactive-bindings/src/types/doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function observeDoc(doc: Y.Doc) {

const originalGet = doc.get;

doc.get = function (key: string) {
doc.get = function(key: string) {
if (typeof key !== "string") {
throw new Error("unexpected");
}
Expand Down
35 changes: 20 additions & 15 deletions packages/yjs-reactive-bindings/src/types/text.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
import { Atom, createAtom } from "../observableProvider";
import * as Y from "yjs";

const textAtoms = new WeakMap<Y.Text, Atom>();
const textsObserved = new WeakSet<Y.Text>();

export function observeText(value: Y.Text) {
let atom = textAtoms.get(value);
if (!atom) {
const handler = (_changes: Y.YTextEvent) => {
atom!.reportChanged();
};
atom = createAtom(
"text",
() => {
value.observe(handler);
},
() => {
value.unobserve(handler);
}
);
if (textsObserved.has(value)) {
// already patched
return value;
}
textsObserved.add(value);

let atom: Atom | undefined;

const handler = (_changes: Y.YTextEvent) => {
atom!.reportChanged();
};
atom = createAtom(
"text",
() => {
value.observe(handler);
},
() => {
value.unobserve(handler);
}
);

function patch(method: string) {
const originalFunction = value[method];
Expand Down
51 changes: 28 additions & 23 deletions packages/yjs-reactive-bindings/src/types/xml.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
import * as Y from "yjs";
import { Atom, createAtom } from "../observableProvider";

const xmlAtoms = new WeakMap<Y.XmlFragment | Y.XmlText, Atom>();
const xmlsObserved = new WeakSet<Y.XmlFragment>();

export function observeXml(value: Y.XmlFragment) {
let atom = xmlAtoms.get(value);
if (!atom) {
const handler = (event: Y.YXmlEvent) => {
if (
event.changes.added.size ||
event.changes.deleted.size ||
event.changes.keys.size ||
event.changes.delta.length
) {
atom!.reportChanged();
}
};

atom = createAtom(
"xml",
() => {
value.observe(handler);
},
() => {
value.unobserve(handler);
}
);
if (xmlsObserved.has(value)) {
// already patched
return value;
}
xmlsObserved.add(value);

let atom: Atom | undefined;

const handler = (event: Y.YXmlEvent) => {
if (
event.changes.added.size ||
event.changes.deleted.size ||
event.changes.keys.size ||
event.changes.delta.length
) {
atom!.reportChanged();
}
};

atom = createAtom(
"xml",
() => {
value.observe(handler);
},
() => {
value.unobserve(handler);
}
);

function patch(method: string) {
const originalFunction = value[method];
Expand Down

0 comments on commit f04255c

Please sign in to comment.