This repository has been archived by the owner on Jul 11, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
[...all].vue
170 lines (151 loc) · 4.48 KB
/
[...all].vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<script setup lang="ts">
import * as Y from 'yjs'
import { redo, undo, ySyncPlugin } from 'y-prosemirror'
import { EditorState } from 'prosemirror-state'
import { EditorView } from 'prosemirror-view'
// @ts-expect-error 'yeet'
import { exampleSetup } from 'prosemirror-example-setup'
// @ts-expect-error 'yeet'
import { keymap } from 'prosemirror-keymap'
import type { ReceivedStatusUpdate } from 'webxdc'
import { schema } from '~/schema'
import '~/styles/style.css'
import { Schema } from 'prosemirror-model'
import type { Ref } from 'vue'
import { toggleDark } from '~/composables/dark'
interface Payload {
updates: any[]
sender: string
}
const ydoc = new Y.Doc()
let prosemirror: EditorView<Schema<"blockquote" | "text" | "doc" | "paragraph" | "heading" | "hard_break", "code" | "em" | "strong" | "ychange">>
const type = ydoc.getXmlFragment('prosemirror')
let initialized = false
let skip_sending = false
const unique_id = window.webxdc.selfAddr + Date.now()
// collect many updates from yjs for debouncing
const updates: Ref<Uint8Array[]> = ref([])
const DEBOUNCE_TIME = 5000
let timeOut: NodeJS.Timeout
// this gets called on every keystroke
ydoc.on('update', (update) => {
saveState()
if (initialized && !skip_sending) {
updates.value.push(update)
if (timeOut) {
clearTimeout(timeOut)
}
timeOut = setTimeout(sendUpdate, DEBOUNCE_TIME)
}
else {
console.log('skipping resend');
};
skip_sending = false
})
// actually sends the collected updates with deltachet
function sendUpdate() {
if (updates.value.length > 0) {
window.webxdc.sendUpdate({
payload: {
updates: Object.assign([], updates.value),
sender:
unique_id
},
summary: prosemirror.state.doc.content.firstChild!.textContent
},
'')
updates.value = []
}
}
function updateSerial(new_serial: number) {
localStorage.setItem('serial', new_serial.toString())
}
// saves the state of the editor and last seen serial number to local storage
function saveState() {
console.log('saving state');
const state_encoded = Y.encodeStateAsUpdate(ydoc)
localStorage.setItem('state', JSON.stringify({ state: state_encoded }))
}
// tries to restore state from local storage
function restoreState() {
const item = localStorage.getItem('state')
if (item !== null) {
const state = JSON.parse(item).state
Y.applyUpdate(ydoc, state)
} else {
throw new Error('no state to restore')
}
}
// receive an update from another client over deltachat
function receiveUpdate(update: ReceivedStatusUpdate<Payload>) {
if (update.payload.sender !== unique_id) {
console.log('applying update', update.payload.updates);
for (const ydoc_update of update.payload.updates) {
skip_sending = true
Y.applyUpdate(ydoc, ydoc_update)
}
}
updateSerial(update.serial)
saveState()
}
let menuBarRef = ref()
onMounted(() => {
const editor = document.querySelector('#editor')!
// @ts-expect-error 'hi'
prosemirror = new EditorView(editor, {
state: EditorState.create({
schema,
plugins: [
//yUndoPlugin(),
ySyncPlugin(type),
keymap({
'Mod-z': undo,
'Mod-y': redo,
'Mod-Shift-z': redo,
}),
].concat(exampleSetup({ schema })),
}),
})
menuBarRef.value = document.getElementsByClassName('ProseMirror-menubar')[0]
window.addEventListener("unload", () => {
if (updates.value.length > 0) {
console.log('automatic sending of queued updates before close');
sendUpdate()
}
})
const latest_serial = localStorage.getItem('serial')
console.log('latest serial', latest_serial)
if (latest_serial !== null) {
console.log('restoring state');
restoreState()
window.webxdc.setUpdateListener(receiveUpdate, parseInt(latest_serial)).then(() => {
initialized = true
})
} else {
window.webxdc.setUpdateListener(receiveUpdate, 0).then(() => {
initialized = true
})
}
})
</script>
<template lang="pug">
div
div(id="editor" class="dark:bg-red")
teleport(v-if="menuBarRef" :to="menuBarRef" )
span.float-right.flex.items-center.h-full
transition-group
button.mr-2.bg-gray-200.rounded.leading-none.p-1#sync(key="1" v-if="updates.length != 0" @click="sendUpdate")
span(i="carbon-send")
button(@click="() => toggleDark()" key="2")
div(i="carbon-sun dark:carbon-moon")
</template>
<style>
.v-enter-active,
.v-leave-active {
transition: opacity 0.2s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>