Skip to content

Commit e09fa3d

Browse files
committed
chore(vue-model-api): restore old useReplicatedModel implementation as a fallback for now
1 parent 39b8ea8 commit e09fa3d

File tree

2 files changed

+130
-67
lines changed

2 files changed

+130
-67
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import type { org } from "@modelix/model-client";
2+
import type { INodeJS } from "@modelix/ts-model-api";
3+
import { useLastPromiseEffect } from "./internal/useLastPromiseEffect";
4+
import type { MaybeRefOrGetter, Ref } from "vue";
5+
import { shallowRef, toValue } from "vue";
6+
import type { ReactiveINodeJS } from "./internal/ReactiveINodeJS";
7+
import { toReactiveINodeJS } from "./internal/ReactiveINodeJS";
8+
import { Cache } from "./internal/Cache";
9+
import { handleChange } from "./internal/handleChange";
10+
11+
type ClientJS = org.modelix.model.client2.ClientJS;
12+
type ReplicatedModelJS = org.modelix.model.client2.ReplicatedModelJS;
13+
type ChangeJS = org.modelix.model.client2.ChangeJS;
14+
15+
function isDefined<T>(value: T | null | undefined): value is T {
16+
return value !== null && value !== undefined;
17+
}
18+
19+
/**
20+
* Creates a replicated model for a given repository and branch.
21+
* A replicated model exposes a branch that can be used to read and write model data.
22+
* The written model data is automatically synced to the model server.
23+
* Changed from the model server are automatically synced to the branch in the replicated model
24+
*
25+
* Also creates root node that uses Vues reactivity and can be used in Vue like a reactive object.
26+
* Changes to model data trigger recalculation of computed properties or re-rendering of components using that data.
27+
*
28+
* Calling the returned dispose function stops syncing the root node to the underlying branch on the server.
29+
*
30+
* @param client - Reactive reference of a client to a model server.
31+
* @param repositoryId - Reactive reference of a repositoryId on the model server.
32+
* @param branchId - Reactive reference of a branchId in the repository of the model server.
33+
*
34+
* @returns {Object} values Wrapper around different returned values.
35+
* @returns {Ref<ReplicatedModelJS | null>} values.rootNode Reactive reference to the replicated model for the specified branch.
36+
* @returns {Ref<INodeJS | null>} values.rootNode Reactive reference to the root node with Vue.js reactivity for the specified branch.
37+
* @returns {() => void} values.dispose A function to manually dispose the root node.
38+
* @returns {Ref<unknown>} values.error Reactive reference to a connection error.
39+
*/
40+
export function useReplicatedModel(
41+
client: MaybeRefOrGetter<ClientJS | null | undefined>,
42+
repositoryId: MaybeRefOrGetter<string | null | undefined>,
43+
branchId: MaybeRefOrGetter<string | null | undefined>,
44+
idScheme: MaybeRefOrGetter<
45+
org.modelix.model.client2.IdSchemeJS | null | undefined
46+
>,
47+
): {
48+
replicatedModel: Ref<ReplicatedModelJS | null>;
49+
rootNode: Ref<INodeJS | null>;
50+
dispose: () => void;
51+
error: Ref<unknown>;
52+
} {
53+
// Use `replicatedModel` to access the replicated model without tracking overhead of Vue.js.
54+
let replicatedModel: ReplicatedModelJS | null = null;
55+
const replicatedModelRef: Ref<ReplicatedModelJS | null> = shallowRef(null);
56+
const rootNodeRef: Ref<INodeJS | null> = shallowRef(null);
57+
const errorRef: Ref<unknown> = shallowRef(null);
58+
59+
const dispose = () => {
60+
// Using `replicatedModelRef.value` here would create a circular dependency.
61+
// `toRaw` does not work on `Ref<>`.
62+
if (replicatedModel !== null) {
63+
replicatedModel.dispose();
64+
}
65+
replicatedModelRef.value = null;
66+
rootNodeRef.value = null;
67+
errorRef.value = null;
68+
};
69+
70+
useLastPromiseEffect(
71+
() => {
72+
dispose();
73+
const clientValue = toValue(client);
74+
if (!isDefined(clientValue)) {
75+
return;
76+
}
77+
const repositoryIdValue = toValue(repositoryId);
78+
if (!isDefined(repositoryIdValue)) {
79+
return;
80+
}
81+
const branchIdValue = toValue(branchId);
82+
if (!isDefined(branchIdValue)) {
83+
return;
84+
}
85+
const idSchemeValue = toValue(idScheme);
86+
if (!isDefined(idSchemeValue)) {
87+
return;
88+
}
89+
const cache = new Cache<ReactiveINodeJS>();
90+
return clientValue
91+
.startReplicatedModel(repositoryIdValue, branchIdValue, idSchemeValue)
92+
.then((replicatedModel) => ({ replicatedModel, cache }));
93+
},
94+
(
95+
{ replicatedModel: connectedReplicatedModel, cache },
96+
isResultOfLastStartedPromise,
97+
) => {
98+
if (isResultOfLastStartedPromise) {
99+
replicatedModel = connectedReplicatedModel;
100+
const branch = replicatedModel.getBranch();
101+
branch.addListener((change: ChangeJS) => {
102+
if (cache === null) {
103+
throw Error("The cache is unexpectedly not set up.");
104+
}
105+
handleChange(change, cache);
106+
});
107+
const unreactiveRootNode = branch.rootNode;
108+
const reactiveRootNode = toReactiveINodeJS(unreactiveRootNode, cache);
109+
replicatedModelRef.value = replicatedModel;
110+
rootNodeRef.value = reactiveRootNode;
111+
} else {
112+
connectedReplicatedModel.dispose();
113+
}
114+
},
115+
(reason, isResultOfLastStartedPromise) => {
116+
if (isResultOfLastStartedPromise) {
117+
errorRef.value = reason;
118+
}
119+
},
120+
);
121+
122+
return {
123+
replicatedModel: replicatedModelRef,
124+
rootNode: rootNodeRef,
125+
dispose,
126+
error: errorRef,
127+
};
128+
}

vue-model-api/src/useReplicatedModels.ts

Lines changed: 2 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { org } from "@modelix/model-client";
1+
import type { org } from "@modelix/model-client";
22
import type { INodeJS } from "@modelix/ts-model-api";
33
import { useLastPromiseEffect } from "./internal/useLastPromiseEffect";
44
import type { MaybeRefOrGetter, Ref } from "vue";
5-
import { shallowRef, toValue, computed } from "vue";
5+
import { shallowRef, toValue } from "vue";
66
import type { ReactiveINodeJS } from "./internal/ReactiveINodeJS";
77
import { toReactiveINodeJS } from "./internal/ReactiveINodeJS";
88
import { Cache } from "./internal/Cache";
@@ -120,68 +120,3 @@ export function useReplicatedModels(
120120
error: errorRef,
121121
};
122122
}
123-
124-
/**
125-
* Creates a replicated model for a given repository and branch.
126-
* A replicated model exposes a branch that can be used to read and write model data.
127-
* The written model data is automatically synced to the model server.
128-
* Changed from the model server are automatically synced to the branch in the replicated model
129-
*
130-
* Also creates root node that uses Vues reactivity and can be used in Vue like a reactive object.
131-
* Changes to model data trigger recalculation of computed properties or re-rendering of components using that data.
132-
*
133-
* Calling the returned dispose function stops syncing the root node to the underlying branch on the server.
134-
*
135-
* @param client - Reactive reference of a client to a model server.
136-
* @param repositoryId - Reactive reference of a repositoryId on the model server.
137-
* @param branchId - Reactive reference of a branchId in the repository of the model server.
138-
*
139-
* @returns {Object} values Wrapper around different returned values.
140-
* @returns {Ref<ReplicatedModelJS | null>} values.rootNode Reactive reference to the replicated model for the specified branch.
141-
* @returns {Ref<INodeJS | null>} values.rootNode Reactive reference to the root node with Vue.js reactivity for the specified branch.
142-
* @returns {() => void} values.dispose A function to manually dispose the root node.
143-
* @returns {Ref<unknown>} values.error Reactive reference to a connection error.
144-
*
145-
* @deprecated Use {@link useReplicatedModels} instead.
146-
*/
147-
export function useReplicatedModel(
148-
client: MaybeRefOrGetter<ClientJS | null | undefined>,
149-
repositoryId: MaybeRefOrGetter<string | null | undefined>,
150-
branchId: MaybeRefOrGetter<string | null | undefined>,
151-
idScheme: MaybeRefOrGetter<
152-
org.modelix.model.client2.IdSchemeJS | null | undefined
153-
>,
154-
): {
155-
replicatedModel: Ref<ReplicatedModelJS | null>;
156-
rootNode: Ref<INodeJS | null>;
157-
dispose: () => void;
158-
error: Ref<unknown>;
159-
} {
160-
const models = computed(() => {
161-
const repositoryIdValue = toValue(repositoryId);
162-
const branchIdValue = toValue(branchId);
163-
const idSchemeValue = toValue(idScheme);
164-
if (!repositoryIdValue || !branchIdValue || !idSchemeValue) {
165-
return null;
166-
}
167-
return [
168-
new org.modelix.model.client2.ReplicatedModelParameters(
169-
repositoryIdValue,
170-
branchIdValue,
171-
idSchemeValue,
172-
),
173-
];
174-
});
175-
176-
const result = useReplicatedModels(client, models);
177-
178-
// Extract the single root node from the array for backward compatibility
179-
const rootNode = computed(() => result.rootNodes.value[0] ?? null);
180-
181-
return {
182-
replicatedModel: result.replicatedModel,
183-
rootNode: rootNode,
184-
dispose: result.dispose,
185-
error: result.error,
186-
};
187-
}

0 commit comments

Comments
 (0)