sync form
This commit is contained in:
parent
1a2647a7b8
commit
d9ca5e3102
3 changed files with 53 additions and 23 deletions
|
@ -1,13 +1,13 @@
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { useConnectionStore } from "./connection";
|
import { useConnectionStore } from "./connection";
|
||||||
import * as Y from "yjs";
|
import * as Y from "yjs";
|
||||||
import { Awareness } from "y-protocols/awareness.js";
|
import * as AwarenessProtocol from "y-protocols/awareness.js";
|
||||||
import { computed, ref } from "vue";
|
|
||||||
|
|
||||||
export const useMissionDetailStore = defineStore("missionDetail", {
|
export const useMissionDetailStore = defineStore("missionDetail", {
|
||||||
state: () => {
|
state: () => {
|
||||||
return {
|
return {
|
||||||
yDoc: new Y.Doc(),
|
yDoc: new Y.Doc(),
|
||||||
|
awareness: undefined as undefined | AwarenessProtocol.Awareness,
|
||||||
docId: null as null | string,
|
docId: null as null | string,
|
||||||
lastUpdateTimestamp: 0 as number,
|
lastUpdateTimestamp: 0 as number,
|
||||||
connectionStatus: "disconnected", // 'disconnected', 'connecting', 'connected', 'syncing', 'synced'
|
connectionStatus: "disconnected", // 'disconnected', 'connecting', 'connected', 'syncing', 'synced'
|
||||||
|
@ -18,16 +18,18 @@ export const useMissionDetailStore = defineStore("missionDetail", {
|
||||||
this.docId = docId;
|
this.docId = docId;
|
||||||
|
|
||||||
this.lastUpdateTimestamp = this.loadLastUpdateFromLocalStorage();
|
this.lastUpdateTimestamp = this.loadLastUpdateFromLocalStorage();
|
||||||
|
this.awareness = new AwarenessProtocol.Awareness(this.yDoc);
|
||||||
|
this.awareness.setLocalStateField("user", { name: "hi", color: "#123456" });
|
||||||
|
|
||||||
this.setupSocketHandlers();
|
this.setupSocketHandlers();
|
||||||
this.setupYjsObservers();
|
this.setupYjsObservers();
|
||||||
|
this.setupAwarenessObservers();
|
||||||
},
|
},
|
||||||
|
|
||||||
setupSocketHandlers() {
|
setupSocketHandlers() {
|
||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
if (!connectionStore.connection) return;
|
|
||||||
|
|
||||||
connectionStore.connection.on("package-sync", (data) => {
|
connectionStore.connection?.on("package-sync", (data) => {
|
||||||
try {
|
try {
|
||||||
this.connectionStatus = "syncing";
|
this.connectionStatus = "syncing";
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ export const useMissionDetailStore = defineStore("missionDetail", {
|
||||||
this.requestFullSync();
|
this.requestFullSync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connectionStore.connection.on("sync-get-missing-updates", (data) => {
|
connectionStore.connection?.on("sync-get-missing-updates", (data) => {
|
||||||
const clientUpdates = Y.encodeStateAsUpdate(this.yDoc, new Uint8Array(data.stateVector));
|
const clientUpdates = Y.encodeStateAsUpdate(this.yDoc, new Uint8Array(data.stateVector));
|
||||||
|
|
||||||
connectionStore.connection?.emit("mission:sync-client-updates", {
|
connectionStore.connection?.emit("mission:sync-client-updates", {
|
||||||
|
@ -53,12 +55,16 @@ export const useMissionDetailStore = defineStore("missionDetail", {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connectionStore.connection?.on("package-sync-awareness", (data) => {
|
||||||
|
// if (this.awareness != undefined) {
|
||||||
|
// AwarenessProtocol.applyAwarenessUpdate(this.awareness, new Uint8Array(data.update), this);
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
||||||
this.joinDocument();
|
this.joinDocument();
|
||||||
},
|
},
|
||||||
|
|
||||||
setupYjsObservers() {
|
setupYjsObservers() {
|
||||||
if (!this.yDoc) return;
|
|
||||||
|
|
||||||
this.yDoc.on("update", (update) => {
|
this.yDoc.on("update", (update) => {
|
||||||
const connectionStore = useConnectionStore();
|
const connectionStore = useConnectionStore();
|
||||||
if (connectionStore.connected) {
|
if (connectionStore.connected) {
|
||||||
|
@ -73,20 +79,35 @@ export const useMissionDetailStore = defineStore("missionDetail", {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
joinDocument() {
|
setupAwarenessObservers() {
|
||||||
const connectionStore = useConnectionStore();
|
if (!this.awareness) return;
|
||||||
if (!connectionStore.connection || !this.docId) return;
|
|
||||||
|
|
||||||
connectionStore.connection.emit("mission:join", this.docId, {
|
this.awareness.on("update", (update: { added: number[]; updated: number[]; removed: number[] }) => {
|
||||||
|
if (this.awareness != undefined) {
|
||||||
|
const changedClients = update.added.concat(update.updated).concat(update.removed);
|
||||||
|
const connectionStore = useConnectionStore();
|
||||||
|
connectionStore.connection?.emit(
|
||||||
|
"mission:sync-client-awareness",
|
||||||
|
Array.from(AwarenessProtocol.encodeAwarenessUpdate(this.awareness, changedClients))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
joinDocument() {
|
||||||
|
if (!this.docId) return;
|
||||||
|
|
||||||
|
const connectionStore = useConnectionStore();
|
||||||
|
connectionStore.connection?.emit("mission:join", this.docId, {
|
||||||
timestamp: this.lastUpdateTimestamp,
|
timestamp: this.lastUpdateTimestamp,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
requestFullSync() {
|
requestFullSync() {
|
||||||
const connectionStore = useConnectionStore();
|
if (!this.docId) return;
|
||||||
if (!connectionStore.connection || !this.docId) return;
|
|
||||||
|
|
||||||
connectionStore.connection.emit("mission:join", this.docId, null);
|
const connectionStore = useConnectionStore();
|
||||||
|
connectionStore.connection?.emit("mission:join", this.docId, null);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadLastUpdateFromLocalStorage() {
|
loadLastUpdateFromLocalStorage() {
|
||||||
|
@ -97,16 +118,18 @@ export const useMissionDetailStore = defineStore("missionDetail", {
|
||||||
},
|
},
|
||||||
|
|
||||||
saveLastUpdateToLocalStorage() {
|
saveLastUpdateToLocalStorage() {
|
||||||
if (!this.docId) return;
|
|
||||||
|
|
||||||
localStorage.setItem(`yjsDoc_timestamp`, this.lastUpdateTimestamp.toString());
|
localStorage.setItem(`yjsDoc_timestamp`, this.lastUpdateTimestamp.toString());
|
||||||
},
|
},
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
if (this.yDoc) {
|
if (this.awareness) {
|
||||||
this.yDoc.destroy();
|
AwarenessProtocol.removeAwarenessStates(this.awareness, [this.yDoc.clientID], "window unload");
|
||||||
this.yDoc = new Y.Doc();
|
this.awareness.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.yDoc.destroy();
|
||||||
|
this.yDoc = new Y.Doc();
|
||||||
|
|
||||||
this.lastUpdateTimestamp = 0;
|
this.lastUpdateTimestamp = 0;
|
||||||
localStorage.removeItem("yjsDoc_timestamp");
|
localStorage.removeItem("yjsDoc_timestamp");
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,6 @@
|
||||||
:style="!can('create', 'operation', 'mission') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
|
:style="!can('create', 'operation', 'mission') ? 'opacity: 75%; background: rgb(243 244 246)' : ''"
|
||||||
@ready="initEditor"
|
@ready="initEditor"
|
||||||
/>
|
/>
|
||||||
<!--v-model:content=""-->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<p>Eingesetzte Fahrzeuge</p>
|
<p>Eingesetzte Fahrzeuge</p>
|
||||||
|
@ -86,6 +85,7 @@ import { moduleOptions } from "@/helpers/quillConfig";
|
||||||
import ForceSelect from "@/components/admin/ForceSelect.vue";
|
import ForceSelect from "@/components/admin/ForceSelect.vue";
|
||||||
import { useForceStore } from "@/stores/admin/configuration/force";
|
import { useForceStore } from "@/stores/admin/configuration/force";
|
||||||
import * as Y from "yjs";
|
import * as Y from "yjs";
|
||||||
|
import type { Awareness } from "y-protocols/awareness.js";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
@ -95,6 +95,10 @@ export default defineComponent({
|
||||||
type: Object as PropType<Y.Doc>,
|
type: Object as PropType<Y.Doc>,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
awareness: {
|
||||||
|
type: Object as PropType<Awareness | undefined>,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -126,7 +130,7 @@ export default defineComponent({
|
||||||
methods: {
|
methods: {
|
||||||
initEditor(quill: Quill) {
|
initEditor(quill: Quill) {
|
||||||
quill.history.clear();
|
quill.history.clear();
|
||||||
this.binding = new QuillBinding(this.editor, quill);
|
this.binding = new QuillBinding(this.editor, quill, this.awareness);
|
||||||
this.cursors = quill.getModule("cursors") as QuillCursors;
|
this.cursors = quill.getModule("cursors") as QuillCursors;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MissionDetail v-show="routeHash == '#edit'" :document="yDoc" />
|
<MissionDetail v-show="routeHash == '#edit'" :document="yDoc" :awareness="awareness" />
|
||||||
<MissionPresence v-show="routeHash == '#presence'" />
|
<MissionPresence v-show="routeHash == '#presence'" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,10 +77,13 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(useConnectionStore, ["connectionStatus"]),
|
...mapState(useConnectionStore, ["connectionStatus"]),
|
||||||
...mapWritableState(useMissionDetailStore, ["yDoc"]),
|
...mapWritableState(useMissionDetailStore, ["yDoc", "awareness"]),
|
||||||
routeHash() {
|
routeHash() {
|
||||||
return this.$route.hash;
|
return this.$route.hash;
|
||||||
},
|
},
|
||||||
|
editors() {
|
||||||
|
return this.awareness?.getStates() ?? [];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.manageHash();
|
this.manageHash();
|
||||||
|
|
Loading…
Add table
Reference in a new issue