parent
a5044cdb44
commit
63646f3e1a
@ -0,0 +1,84 @@ |
||||
import updateShape from "src/ng-mock/wasm-land/updateShape"; |
||||
import type { Connection, Diff, Scope, Shape } from "../types"; |
||||
import requestShape from "src/ng-mock/wasm-land/requestShape"; |
||||
import { applyDiff } from "./applyDiff"; |
||||
import { batch, deepSignal, watch } from "alien-deepsignals"; |
||||
import { signal as createSignal, computed } from "alien-signals"; |
||||
|
||||
type ReactiveShapeObject = object; |
||||
|
||||
type ShapeObjectSignal = ReturnType< |
||||
typeof createSignal<{ |
||||
content: ReactiveShapeObject | null; |
||||
}> |
||||
>; |
||||
|
||||
const openConnections: Partial<Record<Shape, ShapeObjectSignal>> = {}; |
||||
|
||||
/** |
||||
* Create a signal for a shape object. |
||||
* The function returns a signal of a shape object in the form: |
||||
* `{content: <shapeObject>}` |
||||
**/ |
||||
export function createSignalObjectForShape( |
||||
shape: Shape, |
||||
scope?: Scope, |
||||
poolSignal = true |
||||
) { |
||||
if (poolSignal && openConnections[shape]) return openConnections[shape]; |
||||
|
||||
// DeepSignal has a different API to alien-signals.
|
||||
// Therefore, we need to create a "root signal" wrapper that is
|
||||
// triggered on deepSignal changes.
|
||||
const rootSignal = createSignal<{ |
||||
content: ReactiveShapeObject | null; |
||||
}>({ content: null }); |
||||
|
||||
// State
|
||||
let stopWatcher: any = null; |
||||
let suspendDeepWatcher = false; |
||||
|
||||
const onUpdateFromDb = (diff: Diff, connectionId: Connection["id"]) => { |
||||
const rootSignalValue = rootSignal(); |
||||
console.log("Update received", connectionId, diff); |
||||
// Set new value from applying the diffs to the old value.
|
||||
|
||||
suspendDeepWatcher = true; |
||||
// We need to replace the root signal for now, so this is redundant.
|
||||
batch(() => { |
||||
if (!rootSignalValue) return; // This shouldn't happen but we make the compiler happy.
|
||||
const { content: proxiedShapeObj } = rootSignalValue; |
||||
applyDiff(proxiedShapeObj, diff); |
||||
|
||||
// We put the proxied object into a new object for the root signal to trigger.
|
||||
rootSignal({ content: proxiedShapeObj }); |
||||
}); |
||||
suspendDeepWatcher = false; |
||||
}; |
||||
|
||||
// Do the actual db request.
|
||||
requestShape(shape, scope, onUpdateFromDb).then( |
||||
({ connectionId, shapeObject }) => { |
||||
// Create a deepSignal to put into the vanilla alien-signal.
|
||||
const proxiedShapeObj = deepSignal(shapeObject); |
||||
|
||||
// Notify DB on changes.
|
||||
stopWatcher = watch( |
||||
proxiedShapeObj, |
||||
(newVal, oldVal, onCleanup) => { |
||||
// Don't update when applying changes from db diffs from the db.
|
||||
if (!suspendDeepWatcher) updateShape(connectionId, newVal); |
||||
}, |
||||
{ deep: true } |
||||
); |
||||
|
||||
// Update the root signal.
|
||||
rootSignal({ content: proxiedShapeObj }); |
||||
} |
||||
); |
||||
|
||||
if (poolSignal) openConnections[shape] = rootSignal; |
||||
|
||||
// TODO: Dispose deepSignal root signal disposal.
|
||||
return rootSignal; |
||||
} |
@ -1,75 +0,0 @@ |
||||
import updateShape from "src/ng-mock/wasm-land/updateShape"; |
||||
import type { Connection, Diff, Scope, Shape } from "../types"; |
||||
import requestShape from "src/ng-mock/wasm-land/requestShape"; |
||||
import { applyDiff } from "./applyDiff"; |
||||
import { batch, deepSignal, watch } from "alien-deepsignals"; |
||||
import { signal } from "alien-signals"; |
||||
|
||||
const openConnections: Record<Shape, ReturnType<typeof signal>> = {}; |
||||
|
||||
// TODO: The code is horrible.
|
||||
export function createSignalObjectForShape(shape: Shape, scope?: Scope) { |
||||
if (openConnections[shape]) return openConnections[shape]; |
||||
|
||||
// TODO:
|
||||
// DeepSignal has a different API to alien-signals.
|
||||
// Therefore, we need to create a "root signal" wrapper that is
|
||||
// triggered on deepSignal changes.
|
||||
const rootSignal = signal<null | object>(null); |
||||
|
||||
// State
|
||||
let stopWatcher: any = null; |
||||
let suspendWatcher = false; |
||||
|
||||
// To update the root signal
|
||||
const setUpDeepSignal = ( |
||||
newSignal: ReturnType<typeof deepSignal>, |
||||
connectionId: Connection["id"] |
||||
) => { |
||||
stopWatcher?.(); |
||||
|
||||
// Notify DB on changes.
|
||||
stopWatcher = watch( |
||||
newSignal, |
||||
(newVal, oldVal, onCleanup) => { |
||||
if (!suspendWatcher) updateShape(connectionId, newVal); |
||||
}, |
||||
{ deep: true } |
||||
); |
||||
|
||||
// Update the root signal.
|
||||
rootSignal(newSignal); |
||||
}; |
||||
|
||||
const onUpdateFromDb = (diff: Diff, connectionId: Connection["id"]) => { |
||||
const nestedObj = rootSignal(); |
||||
console.log("Update received", connectionId, diff); |
||||
// Set new value from applying the diffs to the old value.
|
||||
|
||||
// suspendWatcher = true;
|
||||
// We need to replace the root signal for now, so this is redundant.
|
||||
// batch(() => {
|
||||
// if (!nestedObj) return; // This shouldn't happen but we make the compiler happy.
|
||||
// applyDiff(nestedObj, diff);
|
||||
// });
|
||||
// suspendWatcher = false;nestedObj
|
||||
|
||||
// Create a new deep signal.
|
||||
// We need to do that because the deepSignals ref hasn't changed otherwise
|
||||
// and no update is triggered.
|
||||
const newDeepSignal = deepSignal(JSON.parse(JSON.stringify(diff))); |
||||
setUpDeepSignal(newDeepSignal, connectionId); |
||||
}; |
||||
|
||||
// Do the actual db request.
|
||||
requestShape(shape, scope, onUpdateFromDb).then( |
||||
({ connectionId, shapeObject }) => { |
||||
// Create a deepSignal to put into the vanilla alien-signal.
|
||||
const deepSignalFromDb = deepSignal(shapeObject); |
||||
setUpDeepSignal(deepSignalFromDb, connectionId); |
||||
} |
||||
); |
||||
|
||||
openConnections[shape] = rootSignal; |
||||
return rootSignal; |
||||
} |
@ -1,13 +1,14 @@ |
||||
import { useSignal } from "@gn8/alien-signals-vue"; |
||||
import { createSignalObjectForShape } from "src/ng-mock/js-land/connector/ngSignals"; |
||||
import { createSignalObjectForShape } from "src/ng-mock/js-land/connector/createSignalObjectForShape"; |
||||
import type { Scope, Shape } from "src/ng-mock/js-land/types"; |
||||
|
||||
import { computed } from "vue"; |
||||
const useShape = (shape: Shape, scope: Scope) => { |
||||
const signalOfShape = createSignalObjectForShape(shape, scope); |
||||
|
||||
const refOfShape = useSignal(signalOfShape); |
||||
|
||||
return refOfShape; |
||||
// TODO: Maybe `refOfShape.value.content` works too?
|
||||
return computed(() => refOfShape.value.content); |
||||
}; |
||||
|
||||
export default useShape; |
||||
|
Loading…
Reference in new issue