mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-19 23:00:13 +00:00
Fixes to collaborator and documents (#2394)
Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
parent
dcaa0ea9b0
commit
afe44eb034
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@ -153,7 +153,7 @@
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"src/index.ts"
|
||||
"src/__start.ts"
|
||||
],
|
||||
"env": {
|
||||
"SECRET": "secret",
|
||||
|
@ -295,7 +295,7 @@ specifiers:
|
||||
mime-types: ~2.1.34
|
||||
mini-css-extract-plugin: ^2.2.0
|
||||
minio: ^7.0.26
|
||||
mongodb: ^4.9.0
|
||||
mongodb: ^4.11.0
|
||||
pdfkit: ~0.13.0
|
||||
postcss: ^8.3.4
|
||||
postcss-load-config: ^3.1.0
|
||||
@ -332,6 +332,7 @@ specifiers:
|
||||
webpack-dev-server: ^4.7.4
|
||||
ws: ^8.10.0
|
||||
xml2js: ~0.4.23
|
||||
y-prosemirror: 1.0.20
|
||||
y-protocols: ~1.0.5
|
||||
y-websocket: ^1.4.5
|
||||
yjs: ^13.5.42
|
||||
@ -668,6 +669,7 @@ dependencies:
|
||||
webpack-dev-server: 4.11.1_78c1cd1c404fc7ed0a3af68b1f6f4aa1
|
||||
ws: 8.11.0
|
||||
xml2js: 0.4.23
|
||||
y-prosemirror: 1.0.20_3b523a098b7386dd759c4c2b4e06fb42
|
||||
y-protocols: 1.0.5
|
||||
y-websocket: 1.4.5_yjs@13.5.42
|
||||
yjs: 13.5.42
|
||||
@ -11137,7 +11139,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/account.tgz:
|
||||
resolution: {integrity: sha512-8yjt4NrdmP1f+lfABJvi/UMWdJyh7o5ozzLsBxAOoNPPJdv2hfWGkQRlaix24WJY69UMXug587gbLi8kvl1JDQ==, tarball: file:projects/account.tgz}
|
||||
resolution: {integrity: sha512-eATySKTuYgYy7bhtyRcIqpHJwH5XNAhrGqfb7xytdE+Mou2AEV6I7s8LtFWLFGnXUJQEakfNk3sPTLtORivw+w==, tarball: file:projects/account.tgz}
|
||||
name: '@rush-temp/account'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -11818,7 +11820,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/dev-account.tgz:
|
||||
resolution: {integrity: sha512-hwScHxHPdx48SHu+nFqc3XRvNabSYK6jL9xUc8uXxgRjkxI5jwqe6EAPczHdFo0KzPSivFP0WEpjaMkUDzH4WQ==, tarball: file:projects/dev-account.tgz}
|
||||
resolution: {integrity: sha512-wgzK5UaTPJua2WnS9J1gbpsCPZcR+OZn+1Lotkjc8fojELP8HbT8MHwNHTYEx14nPO8cYUFGgMwSKMLYeIOkRA==, tarball: file:projects/dev-account.tgz}
|
||||
name: '@rush-temp/dev-account'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -12064,7 +12066,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/front.tgz:
|
||||
resolution: {integrity: sha512-leV9tNxFFE/Z/vwbqsNGdp5OE8DaditWbMLnToMBAinA9vFc3qQi4y8zx2MX4Ohn0D/D0Xymha3xE/9mvXeUTw==, tarball: file:projects/front.tgz}
|
||||
resolution: {integrity: sha512-hjAySYAWM8LKqnfBRXxesUumH6MmZEbazSbEYp4s67um6rGUATr9kUAppfHm2VCuI2WWTmSn52catVFQhRNCqg==, tarball: file:projects/front.tgz}
|
||||
name: '@rush-temp/front'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -12105,7 +12107,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/generator.tgz:
|
||||
resolution: {integrity: sha512-knz6FRYXSNtm1MU/tTKpG1PwQvePi1uaNqNwKmWRURDg/59PESdFMQadueRbIdgJJ15tlJU4ki8yCHmO6l+n0Q==, tarball: file:projects/generator.tgz}
|
||||
resolution: {integrity: sha512-GBIEqPVyMK0nBCG6rFfwmnXuusGg+6R1xHEUp4Q/6aweSkb1mjz5FeycHN2zGs9j45RYwisAwmZCkFICUOaaNQ==, tarball: file:projects/generator.tgz}
|
||||
name: '@rush-temp/generator'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -12638,7 +12640,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/minio.tgz:
|
||||
resolution: {integrity: sha512-VoxVx9EB3UAH22AGrPedW7GCOMvELT35uTIjYoJM91wAg2AeN2yJBTpgoStYCZVIWT9kmCbgGE6+L2Vi0PT7fA==, tarball: file:projects/minio.tgz}
|
||||
resolution: {integrity: sha512-Q7+heaBlBIIsy/JvFJpRnEjwq5dmItX2YK/ID98hd6C/Vaix0DUvwc5VuSadDNC/ugonds1KIfkGqm16mPgiCA==, tarball: file:projects/minio.tgz}
|
||||
name: '@rush-temp/minio'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13633,7 +13635,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/mongo.tgz:
|
||||
resolution: {integrity: sha512-b5/QncRlKkm/JFot8N/JPXLYjKjKTkBIh8g0tS4PWkA/ENdCIDulcpmvi1Q6UM+ZhP6uSGr08JHnVpPmPPj46g==, tarball: file:projects/mongo.tgz}
|
||||
resolution: {integrity: sha512-Td9qjRbNnbet8a7jMYOTEgaHPL5B6L01ny0wolWKPXtGJMcOD9vkWdC+UJuCJ1OSbMv6vo7hOqXdQXocn5G4Cw==, tarball: file:projects/mongo.tgz}
|
||||
name: '@rush-temp/mongo'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13831,7 +13833,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/pod-account.tgz:
|
||||
resolution: {integrity: sha512-n507oMGA9w0oyJHDG4Ct65c2q/reFMbeOYkqd8GLrV15ggrNObeMt21vKuGmNzcoa275qIUeUdOY5xbDuR4FQg==, tarball: file:projects/pod-account.tgz}
|
||||
resolution: {integrity: sha512-57U5JccEZbAk81q+pwaLqNK7VJAF+soBtVsIVRdKgnlgSuI/zjSVEXyKJT7OyXCQPsrJ4fR/TpP8c+JoLaenDg==, tarball: file:projects/pod-account.tgz}
|
||||
name: '@rush-temp/pod-account'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13867,7 +13869,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/pod-backup.tgz:
|
||||
resolution: {integrity: sha512-lNuroyMRN27WmKY8SOxXDi4crPhzNhS10u4XL6D3LHtip2b3tebu04AdGVloiUOHdKjufcVzGqGQ33ZL/976QA==, tarball: file:projects/pod-backup.tgz}
|
||||
resolution: {integrity: sha512-Olz5vF5waH9Hbb3l39zF4DvevJeCxykLR41PzrVb2CYmfvrF8i34V7HvJ2dXzxUXm+EClkbj31S02adwyfT7kA==, tarball: file:projects/pod-backup.tgz}
|
||||
name: '@rush-temp/pod-backup'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13899,7 +13901,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/pod-collaborator.tgz:
|
||||
resolution: {integrity: sha512-WF2SZB/a+C7SK3N3gC0rKcFvdEy6NSFYTUnY1Xv8fjanS/d0VEjpmEjpgyCBvRy7B9fKSMIN90yrkhf6F1p/uA==, tarball: file:projects/pod-collaborator.tgz}
|
||||
resolution: {integrity: sha512-FM+/tmFMqqJpRV8nfJ3f2Cuz7sLrApZ7I9fpIjB3yPcLwA/UawwR6ew/zBQMEdazCbe6s/NjNBETquptO8f7lg==, tarball: file:projects/pod-collaborator.tgz}
|
||||
name: '@rush-temp/pod-collaborator'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13935,7 +13937,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/pod-front.tgz:
|
||||
resolution: {integrity: sha512-QDIGhzVOhCUdd122OEVtsi7ELilODbEDWKSr78OTdHelqV8CcsgWIWU8RsYBIqAAIIMRoyu1rXQqDzEgwYfSpw==, tarball: file:projects/pod-front.tgz}
|
||||
resolution: {integrity: sha512-oxfotaMR88yF2NtkdbtsEvYVCe+GdtuaqvKrEtDMZlk2gcEMCZge1tWTES+HSIxHtWB4pIH1kfzirk+v7eB5Jw==, tarball: file:projects/pod-front.tgz}
|
||||
name: '@rush-temp/pod-front'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -13977,7 +13979,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/pod-server.tgz:
|
||||
resolution: {integrity: sha512-IpXQo5LRXdiCB4ir/T+i+qWMhCaYaHSiGnrE+w7QYf85tS1NmhNS2PWEvU0CTwQYlTiD5e7PNgok6orsU8pexQ==, tarball: file:projects/pod-server.tgz}
|
||||
resolution: {integrity: sha512-rfkv0eygy8Jmv8uw2KEdIfuP0TrGIgXAIUv6Y/uUn/lvPQR9FBCuDfZ9/sEJW1d7MRaBLeFkmVr0xzivBYZHZA==, tarball: file:projects/pod-server.tgz}
|
||||
name: '@rush-temp/pod-server'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -14295,7 +14297,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-backup.tgz:
|
||||
resolution: {integrity: sha512-snkRz4+41T/IJEpLmAnc9lUqo+K1xa0jkpcB5wdg1YSG+XQupGOuJ4Lu6V63+KrHEbWpircaTNL9gFEHQsWl5w==, tarball: file:projects/server-backup.tgz}
|
||||
resolution: {integrity: sha512-RCwlP7QyaEr5W6rpqf8/cTOR7cH56704M5+w5ZRbX28SrTWoeFssfEmYMEuqLVxQvVcwSySq5wGegV/VHrwSVQ==, tarball: file:projects/server-backup.tgz}
|
||||
name: '@rush-temp/server-backup'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -14402,7 +14404,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-contact-resources.tgz:
|
||||
resolution: {integrity: sha512-RmqLUSQSZYSc1UFyU0RmZ7b7IBMT0rrbF3B1KpGcnFbu+sduTPW5byef5DnRLIDX2YlNvejYsfXssBCNk0LU/w==, tarball: file:projects/server-contact-resources.tgz}
|
||||
resolution: {integrity: sha512-kqkkjCxfo3L3lB6yzXWlGedWYGyi31TQcKR0ZIqYxZxakeB7zUTxSfayEGIxxRvIeJ8K2fXtnkwjk5AbaRKi7Q==, tarball: file:projects/server-contact-resources.tgz}
|
||||
name: '@rush-temp/server-contact-resources'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -14443,7 +14445,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-core.tgz:
|
||||
resolution: {integrity: sha512-CihDLjYbiz3luqKzAoF181km4CZxkxqVzQoxaJ24YPgvv4Fw0ztT3o1fw3f/68mNya4DbE9lrKvETKL3jeLpmw==, tarball: file:projects/server-core.tgz}
|
||||
resolution: {integrity: sha512-dBZNtVbslOApBtMTuf+X50jpjLsCoLmPje51lAr6UgKIaeL9kjhaVWz9AjNA6bCQgBBM+MYe6fbRIPaXCCfmEA==, tarball: file:projects/server-core.tgz}
|
||||
name: '@rush-temp/server-core'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -14897,7 +14899,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-token.tgz:
|
||||
resolution: {integrity: sha512-2obpSEchu1xIse/mgmk2GLor2XtUmF+1CULwme1I1T1ZU50nPcpu8AZrOO7cGK9d34CTJonLDk+wscGA4pt4hQ==, tarball: file:projects/server-token.tgz}
|
||||
resolution: {integrity: sha512-8KGPm5Wes48jUlz/RyGE3rIsQ8nd8S4otlVnZvUB8x1hSRtMngc1DNQVmgCy3iUwNYuyN9w7r0zZN7c3F+Vn5Q==, tarball: file:projects/server-token.tgz}
|
||||
name: '@rush-temp/server-token'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -14921,7 +14923,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server-tool.tgz:
|
||||
resolution: {integrity: sha512-VxPpII/+/u5gj40wFyU/wiV9EevgwadwNF3FFZGsnG4SJmWf34aSvvH4Cz8WilEAzeA4LXlUS20stY+9hFFycg==, tarball: file:projects/server-tool.tgz}
|
||||
resolution: {integrity: sha512-Xm9jJEzgWPgN4FefhPDKMv6MYMW7vAfW74FQ1E3xyli3zNW6UiZje+y46ooDu1MREOFyfTYyIAJkhAx7r1lBdw==, tarball: file:projects/server-tool.tgz}
|
||||
name: '@rush-temp/server-tool'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -15015,7 +15017,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/server.tgz:
|
||||
resolution: {integrity: sha512-1BkV1SV80DpRxzAKpNIcL0yHhybay4GRjjaMU8QGD4PipOn6rO3u0x/iELRA8hHnPJzFDZQcqI2/+hm5h5buGQ==, tarball: file:projects/server.tgz}
|
||||
resolution: {integrity: sha512-L3P1x5JppF1RTeUhn+oM9/8Udx33+C5rKVu3he3DWjbXd5XTvfY8XsiSkZwoBTsYlXsevTZnd5aYA8ZjqPmGRQ==, tarball: file:projects/server.tgz}
|
||||
name: '@rush-temp/server'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -15451,7 +15453,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/text-editor.tgz_89204ec304a9fe9c91bbfc5394a172bd:
|
||||
resolution: {integrity: sha512-jGIekdEoT82BTFc4UVhbLOuQZJkVp4NSb7P3DOw5JJwJ0KvuU0mQZcA7AGKTI7eAuPBG6xjtFR2f4ykt+QcjmA==, tarball: file:projects/text-editor.tgz}
|
||||
resolution: {integrity: sha512-y/a6ITp7adtktGttSf9Fqvxz60+EN3SPpWK/KLeezMmTVdeejIBiNYSsMnJGjWhuJ8PV3WDQVLsbXSRZBwAwLA==, tarball: file:projects/text-editor.tgz}
|
||||
id: file:projects/text-editor.tgz
|
||||
name: '@rush-temp/text-editor'
|
||||
version: 0.0.0
|
||||
@ -15501,6 +15503,7 @@ packages:
|
||||
svelte-loader: 3.1.4_svelte@3.53.1
|
||||
svelte-preprocess: 4.10.7_1cd24d71cb02643c0a6ca17ff2edd158
|
||||
typescript: 4.8.4
|
||||
y-prosemirror: 1.0.20_3b523a098b7386dd759c4c2b4e06fb42
|
||||
y-websocket: 1.4.5_yjs@13.5.42
|
||||
yjs: 13.5.42
|
||||
transitivePeerDependencies:
|
||||
@ -15553,7 +15556,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tool.tgz:
|
||||
resolution: {integrity: sha512-lPQdPlcd6ajld4cEO1nodKIc4avevkjqB1qAYMfrLfN9X+F7WWv+bf3r0OLkDpXurUiufMgCo/KAMhypIeLdJw==, tarball: file:projects/tool.tgz}
|
||||
resolution: {integrity: sha512-zhdyLwe6Bv8T3Pnby/tdJxQZljdHmJqT6w5qBnF+TyoOEPknKxbheB/I/u6IOSq3IJn5ti12Nz02IBReeEsnWQ==, tarball: file:projects/tool.tgz}
|
||||
name: '@rush-temp/tool'
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
@ -15621,7 +15624,7 @@ packages:
|
||||
dev: false
|
||||
|
||||
file:projects/tracker-resources.tgz_49b4785992daa3b61a639b2b31601e76:
|
||||
resolution: {integrity: sha512-mAMoLXcGbvCBFiy0w3I2qDd//xQHlzzNsjkDY9h/xREiAzkdzgusZNu6J/tlYEK0gJGKKXki46a1sH9dBNgIzQ==, tarball: file:projects/tracker-resources.tgz}
|
||||
resolution: {integrity: sha512-ZY4HQHk3DoAHrswKl30DwbWhgun3qmqwN4L4fiG/iu7P0hnKNQpFO9XGgyZrarteHamCj2LayFuhQM/eTU5IQw==, tarball: file:projects/tracker-resources.tgz}
|
||||
id: file:projects/tracker-resources.tgz
|
||||
name: '@rush-temp/tracker-resources'
|
||||
version: 0.0.0
|
||||
|
@ -35,6 +35,7 @@
|
||||
export let isAside: boolean = true
|
||||
export let isCustomAttr: boolean = true
|
||||
export let floatAside = false
|
||||
export let allowClose = true
|
||||
</script>
|
||||
|
||||
<Panel
|
||||
@ -44,6 +45,7 @@
|
||||
bind:innerWidth
|
||||
bind:withoutTitle
|
||||
on:close
|
||||
{allowClose}
|
||||
{floatAside}
|
||||
>
|
||||
<svelte:fragment slot="navigator">
|
||||
|
@ -55,6 +55,7 @@
|
||||
"prosemirror-transform": "~1.7.0",
|
||||
"yjs": "^13.5.42",
|
||||
"y-websocket": "^1.4.5",
|
||||
"y-prosemirror": "1.0.20",
|
||||
"prosemirror-changeset": "~2.2.0",
|
||||
"prosemirror-model": "~1.18.1",
|
||||
"prosemirror-view": "~1.29.0",
|
||||
|
61
packages/text-editor/src/components/Collaboration.svelte
Normal file
61
packages/text-editor/src/components/Collaboration.svelte
Normal file
@ -0,0 +1,61 @@
|
||||
<!--
|
||||
//
|
||||
// Copyright © 2022 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onDestroy, setContext } from 'svelte'
|
||||
import { WebsocketProvider } from 'y-websocket'
|
||||
import * as Y from 'yjs'
|
||||
import { CollaborationIds } from '../types'
|
||||
export let documentId: string
|
||||
export let token: string
|
||||
export let collaboratorURL: string
|
||||
|
||||
export let initialContentId: string | undefined = undefined
|
||||
|
||||
let _documentId = ''
|
||||
|
||||
let wsProvider: WebsocketProvider | undefined
|
||||
|
||||
$: if (_documentId !== documentId) {
|
||||
_documentId = documentId
|
||||
if (wsProvider !== undefined) {
|
||||
wsProvider.disconnect()
|
||||
}
|
||||
const ydoc: Y.Doc = new Y.Doc()
|
||||
wsProvider = new WebsocketProvider(collaboratorURL, documentId, ydoc, {
|
||||
params: {
|
||||
token,
|
||||
documentId,
|
||||
initialContentId: initialContentId ?? ''
|
||||
}
|
||||
})
|
||||
setContext(CollaborationIds.Doc, ydoc)
|
||||
setContext(CollaborationIds.Provider, wsProvider)
|
||||
wsProvider.on('status', (event: any) => {
|
||||
console.log(documentId, event.status) // logs "connected" or "disconnected"
|
||||
})
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
setTimeout(() => {
|
||||
wsProvider?.disconnect()
|
||||
}, 100)
|
||||
})
|
||||
</script>
|
||||
|
||||
{#key _documentId}
|
||||
<slot />
|
||||
{/key}
|
@ -124,7 +124,8 @@
|
||||
.ProseMirror {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
max-height: 60vh;
|
||||
min-height: 3rem;
|
||||
max-height: inherit !important;
|
||||
outline: none;
|
||||
line-height: 150%;
|
||||
color: var(--accent-color);
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
import { DecorationSet } from 'prosemirror-view'
|
||||
import textEditorPlugin from '../plugin'
|
||||
import { FormatMode, FORMAT_MODES } from '../types'
|
||||
import { CollaborationIds, FormatMode, FORMAT_MODES } from '../types'
|
||||
import Bold from './icons/Bold.svelte'
|
||||
import Code from './icons/Code.svelte'
|
||||
import CodeBlock from './icons/CodeBlock.svelte'
|
||||
@ -61,22 +61,29 @@
|
||||
import DeleteRow from './icons/table/DeleteRow.svelte'
|
||||
import DeleteCol from './icons/table/DeleteCol.svelte'
|
||||
import DeleteTable from './icons/table/DeleteTable.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
|
||||
export let documentId: string
|
||||
export let readonly = false
|
||||
|
||||
export let token: string
|
||||
export let collaboratorURL: string
|
||||
|
||||
export let isFormatting = true
|
||||
export let buttonSize: IconSize = 'small'
|
||||
export let focusable: boolean = false
|
||||
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
|
||||
export let initialContentId: string | undefined = undefined
|
||||
export let suggestMode = false
|
||||
export let comparedVersion: Markup | undefined = undefined
|
||||
// export let suggestMode = false
|
||||
export let comparedVersion: Markup | ArrayBuffer | undefined = undefined
|
||||
|
||||
const ydoc = new Y.Doc()
|
||||
const wsProvider = new WebsocketProvider(collaboratorURL, documentId, ydoc, {
|
||||
export let field: string | undefined = undefined
|
||||
|
||||
const ydoc = (getContext(CollaborationIds.Doc) as Y.Doc | undefined) ?? new Y.Doc()
|
||||
const contextProvider = getContext(CollaborationIds.Provider) as WebsocketProvider | undefined
|
||||
const wsProvider =
|
||||
contextProvider ??
|
||||
new WebsocketProvider(collaboratorURL, documentId, ydoc, {
|
||||
params: {
|
||||
token,
|
||||
documentId,
|
||||
@ -84,9 +91,11 @@
|
||||
}
|
||||
})
|
||||
|
||||
wsProvider.on('status', (event: any) => {
|
||||
if (contextProvider === undefined) {
|
||||
wsProvider?.on('status', (event: any) => {
|
||||
console.log(documentId, event.status) // logs "connected" or "disconnected"
|
||||
})
|
||||
}
|
||||
|
||||
const currentUser = getCurrentAccount() as EmployeeAccount
|
||||
|
||||
@ -173,8 +182,8 @@
|
||||
let _decoration = DecorationSet.empty
|
||||
let oldContent = ''
|
||||
|
||||
function updateEditor (editor?: Editor, comparedVersion?: Markup): void {
|
||||
const r = calculateDecorations(editor, oldContent, comparedVersion)
|
||||
function updateEditor (editor?: Editor, field?: string, comparedVersion?: Markup | ArrayBuffer): void {
|
||||
const r = calculateDecorations(editor, oldContent, field, comparedVersion)
|
||||
if (r !== undefined) {
|
||||
oldContent = r.oldContent
|
||||
_decoration = r.decorations
|
||||
@ -183,7 +192,7 @@
|
||||
|
||||
const updateDecorations = () => {
|
||||
if (editor && editor.schema) {
|
||||
updateEditor(editor, comparedVersion)
|
||||
updateEditor(editor, field, comparedVersion)
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,7 +215,7 @@
|
||||
}
|
||||
})
|
||||
|
||||
$: updateEditor(editor, comparedVersion)
|
||||
$: updateEditor(editor, field, comparedVersion)
|
||||
|
||||
onMount(() => {
|
||||
ph.then(() => {
|
||||
@ -219,7 +228,8 @@
|
||||
Placeholder.configure({ placeholder: placeHolderStr }),
|
||||
|
||||
Collaboration.configure({
|
||||
document: ydoc
|
||||
document: ydoc,
|
||||
field
|
||||
}),
|
||||
CollaborationCursor.configure({
|
||||
provider: wsProvider,
|
||||
@ -256,9 +266,13 @@
|
||||
|
||||
onDestroy(() => {
|
||||
if (editor) {
|
||||
try {
|
||||
editor.destroy()
|
||||
} catch (err: any) {}
|
||||
if (contextProvider === undefined) {
|
||||
wsProvider.disconnect()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let activeModes = new Set<FormatMode>()
|
||||
@ -573,8 +587,8 @@
|
||||
<style lang="scss" global>
|
||||
.ProseMirror {
|
||||
flex-grow: 1;
|
||||
overflow: auto;
|
||||
max-height: 60vh;
|
||||
min-height: 3rem;
|
||||
max-height: inherit !important;
|
||||
outline: none;
|
||||
line-height: 150%;
|
||||
color: var(--accent-color);
|
||||
|
@ -29,6 +29,7 @@
|
||||
export let previewLimit: number = 240
|
||||
export let previewUnlimit: boolean = false
|
||||
export let focusable: boolean = false
|
||||
export let enableFormatting = false
|
||||
|
||||
const Mode = {
|
||||
View: 1,
|
||||
@ -104,6 +105,7 @@
|
||||
{buttonSize}
|
||||
{maxHeight}
|
||||
{focusable}
|
||||
{enableFormatting}
|
||||
bind:content={rawValue}
|
||||
bind:this={textEditor}
|
||||
on:attach
|
||||
@ -119,6 +121,9 @@
|
||||
}}
|
||||
on:value={(evt) => {
|
||||
rawValue = evt.detail
|
||||
if (alwaysEdit) {
|
||||
content = evt.detail
|
||||
}
|
||||
dispatch('changeContent')
|
||||
}}
|
||||
>
|
||||
|
@ -66,6 +66,7 @@
|
||||
export let focusable: boolean = false
|
||||
export let maxHeight: 'max' | 'card' | 'limited' | string | undefined = undefined
|
||||
export let withoutTopBorder = false
|
||||
export let enableFormatting = false
|
||||
|
||||
let textEditor: TextEditor
|
||||
|
||||
@ -85,7 +86,7 @@
|
||||
? 'max-content'
|
||||
: maxHeight
|
||||
|
||||
let isFormatting = false
|
||||
let isFormatting = enableFormatting
|
||||
let activeModes = new Set<FormatMode>()
|
||||
let isSelectionEmpty = true
|
||||
|
||||
|
@ -216,7 +216,8 @@
|
||||
<style lang="scss" global>
|
||||
.ProseMirror {
|
||||
overflow-y: auto;
|
||||
max-height: 40vh;
|
||||
min-height: 3rem;
|
||||
max-height: inherit !important;
|
||||
outline: none;
|
||||
line-height: 150%;
|
||||
color: var(--accent-color);
|
||||
|
@ -19,16 +19,32 @@ import { ChangeSet } from 'prosemirror-changeset'
|
||||
import { DOMParser, Node, Schema } from 'prosemirror-model'
|
||||
import { Decoration, DecorationSet } from 'prosemirror-view'
|
||||
import { recreateTransform } from './recreate'
|
||||
import { yDocToProsemirrorJSON } from 'y-prosemirror'
|
||||
import { Doc, applyUpdate } from 'yjs'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export function createDocument (schema: Schema, content: Markup): Node {
|
||||
export function createDocument (schema: Schema, content: Markup | ArrayBuffer, field?: string): Node {
|
||||
if (typeof content === 'string') {
|
||||
const wrappedValue = `<body>${content}</body>`
|
||||
|
||||
const body = new window.DOMParser().parseFromString(wrappedValue, 'text/html').body
|
||||
|
||||
return DOMParser.fromSchema(schema).parse(body)
|
||||
} else {
|
||||
try {
|
||||
const ydoc = new Doc()
|
||||
const uint8arr = new Uint8Array(content)
|
||||
applyUpdate(ydoc, uint8arr)
|
||||
|
||||
const body = yDocToProsemirrorJSON(ydoc, field)
|
||||
return schema.nodeFromJSON(body)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
return schema.node(schema.topNodeType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -37,7 +53,8 @@ export function createDocument (schema: Schema, content: Markup): Node {
|
||||
export function calculateDecorations (
|
||||
editor?: Editor,
|
||||
oldContent?: string,
|
||||
comparedVersion?: Markup
|
||||
field?: string,
|
||||
comparedVersion?: Markup | ArrayBuffer
|
||||
):
|
||||
| {
|
||||
decorations: DecorationSet
|
||||
@ -52,7 +69,7 @@ export function calculateDecorations (
|
||||
return
|
||||
}
|
||||
const schema = editor.schema
|
||||
const docOld = createDocument(schema, comparedVersion)
|
||||
const docOld = createDocument(schema, comparedVersion, field)
|
||||
const docNew = editor.state.doc
|
||||
|
||||
const c = editor.getHTML()
|
||||
@ -93,6 +110,7 @@ export function calculateDecorations (
|
||||
if (decorations.length > 0) {
|
||||
return { decorations: DecorationSet.empty.add(docNew, decorations), oldContent: c }
|
||||
}
|
||||
return { decorations: DecorationSet.empty, oldContent: c }
|
||||
} catch (error: any) {
|
||||
console.error(error)
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ export { default as CollaboratorEditor } from './components/CollaboratorEditor.s
|
||||
export { default as CollaborationDiffViewer } from './components/CollaborationDiffViewer.svelte'
|
||||
export { default } from './plugin'
|
||||
export * from './types'
|
||||
export { default as Collaboration } from './components/Collaboration.svelte'
|
||||
|
||||
addStringsLoader(textEditorId, async (lang: string) => {
|
||||
return await import(`../lang/${lang}.json`)
|
||||
|
@ -25,6 +25,9 @@ export interface RefInputActionItem extends Doc {
|
||||
order?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const FORMAT_MODES = [
|
||||
'bold',
|
||||
'italic',
|
||||
@ -39,4 +42,15 @@ export const FORMAT_MODES = [
|
||||
'table'
|
||||
] as const
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type FormatMode = typeof FORMAT_MODES[number]
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const CollaborationIds = {
|
||||
Doc: 'text-editor.collaborator.document',
|
||||
Provider: 'text-editor.collaborator.provider'
|
||||
}
|
||||
|
@ -525,6 +525,7 @@ input.search {
|
||||
.min-h-0 { min-height: 0; }
|
||||
.min-h-2 { min-height: .5rem; }
|
||||
.min-h-7 { min-height: 1.75rem; }
|
||||
.min-h-30 { min-height: 7.5rem; }
|
||||
.min-h-60 { min-height: 15rem; }
|
||||
.max-w-9 { max-width: 2.25rem; }
|
||||
.max-w-30 { max-width: 7.5rem; }
|
||||
|
@ -139,7 +139,7 @@
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
// flex-shrink: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: calc(100% - 7.5rem);
|
||||
@ -171,6 +171,7 @@
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&.between { justify-content: space-between; }
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
export let direction: TooltipAlignment | undefined = undefined
|
||||
export let icon: Asset | AnySvelteComponent
|
||||
export let iconProps: any | undefined = undefined
|
||||
export let size: 'small' | 'medium' | 'large'
|
||||
export let size: 'x-small' | 'small' | 'medium' | 'large'
|
||||
export let action: (ev: MouseEvent) => Promise<void> | void = async () => {}
|
||||
export let invisible: boolean = false
|
||||
</script>
|
||||
|
@ -30,6 +30,7 @@
|
||||
export let isFullSize: boolean = false
|
||||
export let withoutTitle: boolean = false
|
||||
export let floatAside = false
|
||||
export let allowClose = true
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@ -61,6 +62,7 @@
|
||||
>
|
||||
<div class="popupPanel-title__bordered {twoRows && !withoutTitle ? 'flex-col flex-no-shrink' : 'flex-row-center'}">
|
||||
<div class="popupPanel-title {twoRows && !withoutTitle ? 'row-top' : 'row'}">
|
||||
{#if allowClose}
|
||||
<Button
|
||||
icon={IconClose}
|
||||
kind={'transparent'}
|
||||
@ -69,6 +71,7 @@
|
||||
dispatch('close')
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#if $$slots.navigator}<slot name="navigator" />{/if}
|
||||
<div class="popupPanel-title__content">
|
||||
{#if !twoRows && !withoutTitle}<slot name="title" />{/if}
|
||||
|
@ -14,19 +14,20 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, Doc, Ref, Space } from '@hcengineering/core'
|
||||
import { Label, Spinner, Icon } from '@hcengineering/ui'
|
||||
import { Class, Doc, DocumentQuery, Ref, Space } from '@hcengineering/core'
|
||||
import { Icon, Label, Spinner } from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { Table } from '@hcengineering/view-resources'
|
||||
import attachment from '../plugin'
|
||||
import AddAttachment from './AddAttachment.svelte'
|
||||
import IconAttachment from './icons/Attachment.svelte'
|
||||
import AttachmentDroppable from './AttachmentDroppable.svelte'
|
||||
import IconAttachment from './icons/Attachment.svelte'
|
||||
import UploadDuo from './icons/UploadDuo.svelte'
|
||||
|
||||
export let objectId: Ref<Doc>
|
||||
export let space: Ref<Space>
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc> = {}
|
||||
|
||||
export let attachments: number | undefined = undefined
|
||||
|
||||
@ -59,6 +60,7 @@
|
||||
<div class="text-sm dark-color" style:pointer-events="none">
|
||||
<Label label={attachment.string.NoAttachments} />
|
||||
</div>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class="over-underline text-sm content-accent-color"
|
||||
style:pointer-events={dragover ? 'none' : 'all'}
|
||||
@ -83,8 +85,11 @@
|
||||
'lastModified'
|
||||
]}
|
||||
options={{ sort: { pinned: -1 } }}
|
||||
query={{ attachedTo: objectId }}
|
||||
query={{ ...query, attachedTo: objectId }}
|
||||
loadingProps={{ length: attachments ?? 0 }}
|
||||
on:content={(evt) => {
|
||||
attachments = evt.detail.length
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -27,7 +27,6 @@
|
||||
export let object: DocumentVersion
|
||||
export let readonly = false
|
||||
export let initialContentId: string | undefined = undefined
|
||||
export let suggestMode = false
|
||||
export let comparedVersion: Markup | undefined = undefined
|
||||
|
||||
const token = getMetadata(login.metadata.LoginToken) ?? ''
|
||||
@ -42,7 +41,6 @@
|
||||
<CollaboratorEditor
|
||||
documentId={object.contentAttachmentId}
|
||||
{token}
|
||||
{suggestMode}
|
||||
{collaboratorURL}
|
||||
{readonly}
|
||||
on:content
|
||||
|
@ -144,7 +144,7 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
dispatch('open', { ignoreKeys: ['comments', 'name'] })
|
||||
dispatch('open', { ignoreKeys: ['comments', 'name', 'reviewers'] })
|
||||
})
|
||||
|
||||
const versionQuery = createQuery()
|
||||
@ -230,34 +230,6 @@
|
||||
|
||||
let autoSelect = true
|
||||
|
||||
type ModelType = 'view' | 'edit' // | 'suggest'
|
||||
let mode: ModelType = 'view'
|
||||
const modeLabels = {
|
||||
view: document.string.ViewMode,
|
||||
edit: document.string.EditMode
|
||||
// suggest: document.string.SuggestMode
|
||||
}
|
||||
|
||||
function selectMode (event: MouseEvent): void {
|
||||
showPopup(
|
||||
SelectPopup,
|
||||
{
|
||||
value: Object.entries(modeLabels).map(([mode, label]) => ({
|
||||
id: mode,
|
||||
label
|
||||
})),
|
||||
placeholder: document.string.Version,
|
||||
searchable: false
|
||||
},
|
||||
eventToHTMLElement(event),
|
||||
(res) => {
|
||||
if (res != null) {
|
||||
mode = res
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async function doEdit (documentObject: Document): Promise<void> {
|
||||
processing = true
|
||||
// Looking for a draft version
|
||||
@ -319,7 +291,6 @@
|
||||
autoSelect = true
|
||||
}
|
||||
}
|
||||
mode = 'edit'
|
||||
processing = false
|
||||
}
|
||||
|
||||
@ -358,8 +329,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
mode = 'view'
|
||||
|
||||
processing = false
|
||||
}
|
||||
let editor: DocumentEditor
|
||||
@ -479,7 +448,7 @@
|
||||
size={'medium'}
|
||||
disabled={documentObject?.approvers?.length === 0}
|
||||
/>
|
||||
<Button
|
||||
<!-- <Button
|
||||
loading={processing}
|
||||
kind={'link-bordered'}
|
||||
label={document.string.SendForReview}
|
||||
@ -487,7 +456,7 @@
|
||||
icon={IconShare}
|
||||
size={'medium'}
|
||||
disabled={documentObject?.reviewers?.length === 0}
|
||||
/>
|
||||
/> -->
|
||||
{/if}
|
||||
{#if version?.state === DocumentVersionState.Draft && approveRequest}
|
||||
<Button
|
||||
@ -517,13 +486,6 @@
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
{#if !readonly && version?.state === DocumentVersionState.Draft && approveRequest === undefined}
|
||||
<Button loading={processing} kind={'link-bordered'} on:click={selectMode} icon={IconEdit} size={'medium'}>
|
||||
<svelte:fragment slot="content">
|
||||
<Label label={modeLabels[mode]} />
|
||||
</svelte:fragment>
|
||||
</Button>
|
||||
{/if}
|
||||
<Button icon={IconMoreH} kind={'transparent'} size={'medium'} on:click={showMenu} />
|
||||
</svelte:fragment>
|
||||
|
||||
@ -535,7 +497,7 @@
|
||||
object={version}
|
||||
initialContentId={version.initialContentId}
|
||||
comparedVersion={compareTo?.content ?? versions[versions.length - 2]?.content}
|
||||
readonly={mode === 'view'}
|
||||
readonly={false}
|
||||
bind:this={editor}
|
||||
/>
|
||||
{/key}
|
||||
@ -547,7 +509,12 @@
|
||||
</div>
|
||||
|
||||
<div class="p-1 mt-6">
|
||||
<Attachments objectId={documentObject._id} space={documentObject.space} _class={documentObject._class} />
|
||||
<Attachments
|
||||
objectId={documentObject._id}
|
||||
space={documentObject.space}
|
||||
_class={documentObject._class}
|
||||
attachments={documentObject.attachments ?? 0}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="custom-attributes">
|
||||
@ -555,7 +522,7 @@
|
||||
object={documentObject}
|
||||
_class={documentObject._class}
|
||||
to={core.class.Doc}
|
||||
ignoreKeys={['name']}
|
||||
ignoreKeys={['name', 'reviewers']}
|
||||
{readonly}
|
||||
/>
|
||||
|
||||
@ -586,7 +553,7 @@
|
||||
.description-preview {
|
||||
color: var(--theme-content-color);
|
||||
line-height: 150%;
|
||||
overflow: auto;
|
||||
// overflow: auto;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
|
@ -333,7 +333,7 @@
|
||||
{/if}
|
||||
{#each fieldEditors as collection}
|
||||
{#if collection.editor}
|
||||
<div class="mt-6 clear-mins">
|
||||
<div class="mt-6">
|
||||
<Component
|
||||
is={collection.editor}
|
||||
props={{
|
||||
|
@ -61,6 +61,7 @@
|
||||
let loading = 0
|
||||
|
||||
let objects: Doc[] = []
|
||||
let objectsRecieved = false
|
||||
const refs: HTMLElement[] = []
|
||||
|
||||
$: refs.length = objects.length
|
||||
@ -91,6 +92,7 @@
|
||||
query,
|
||||
(result) => {
|
||||
objects = result
|
||||
objectsRecieved = true
|
||||
if (sortingFunction !== undefined) {
|
||||
const sf = sortingFunction
|
||||
objects.sort((a, b) => -1 * sortOrder * sf(a, b))
|
||||
@ -230,7 +232,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
{/if}
|
||||
{#if objects.length}
|
||||
{#if objects.length || objectsRecieved}
|
||||
<tbody>
|
||||
{#each objects as object, row (object._id)}
|
||||
<tr
|
||||
|
@ -42,14 +42,15 @@ export function start (ctx: MeasureContext, port: number, minio: MinioService, h
|
||||
)
|
||||
|
||||
const server = createServer()
|
||||
|
||||
server.on('upgrade', (request: IncomingMessage, socket: any, head: Buffer) => {
|
||||
try {
|
||||
const parsedUrl = new URL('http://host' + (request.url ?? ''))
|
||||
const token = parsedUrl.searchParams.get('token')
|
||||
const payload = decodeToken(token ?? '')
|
||||
console.log('client connected with payload', payload)
|
||||
|
||||
const documentId = parsedUrl.searchParams.get('documentId') as string
|
||||
console.log('client connected with payload', payload, documentId)
|
||||
const initialContentId = parsedUrl.searchParams.get('initialContentId') as string
|
||||
wss.handleUpgrade(request, socket, head, (ws) =>
|
||||
wss.emit('connection', ws, request, payload, documentId, initialContentId)
|
||||
|
@ -52,9 +52,20 @@ persistence = {
|
||||
let minioDocument: Buffer | undefined
|
||||
try {
|
||||
minioDocument = Buffer.concat(await minio.read(token.workspace, documentId))
|
||||
console.log('bind for document', documentId, token.email)
|
||||
} catch (err: any) {
|
||||
if (initialContentId !== undefined && initialContentId.length > 0) {
|
||||
// Try first take existing document.
|
||||
|
||||
const existingDoc = getYDoc(initialContentId, token, true, minio, initialContentId, false)
|
||||
if (existingDoc !== undefined) {
|
||||
const newUpdates = encodeStateAsUpdate(existingDoc)
|
||||
minioDocument = Buffer.from(newUpdates.buffer)
|
||||
console.log('bind for existing document', documentId, token.email)
|
||||
} else {
|
||||
minioDocument = Buffer.concat(await minio.read(token.workspace, initialContentId))
|
||||
console.log('bind for initial document', documentId, token.email, initialContentId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,6 +87,8 @@ persistence = {
|
||||
const buffer = Buffer.from(newUpdates.buffer)
|
||||
|
||||
await ydoc?.minio?.put(token.workspace, documentId, buffer)
|
||||
|
||||
console.log('state written for', documentId, token.email)
|
||||
} catch (err: any) {
|
||||
console.error(err)
|
||||
}
|
||||
@ -180,10 +193,11 @@ export function getYDoc (
|
||||
token: Token,
|
||||
gc = true,
|
||||
minio: MinioService,
|
||||
initialContentId: string
|
||||
): WSSharedDoc {
|
||||
initialContentId: string,
|
||||
allowBind = true
|
||||
): WSSharedDoc | undefined {
|
||||
let doc = docs.get(docId)
|
||||
if (doc === undefined) {
|
||||
if (doc === undefined && allowBind) {
|
||||
doc = new WSSharedDoc(docId)
|
||||
doc.gc = gc
|
||||
docs.set(docId, doc)
|
||||
@ -241,10 +255,10 @@ const closeConn = (doc: WSSharedDoc, conn: any): void => {
|
||||
// if persisted, we store state and destroy ydocument
|
||||
if (controlledIds !== undefined) {
|
||||
void persistence.writeState(doc.name, doc, controlledIds?.token).then(() => {
|
||||
docs.delete(doc.name)
|
||||
doc.destroy()
|
||||
})
|
||||
}
|
||||
docs.delete(doc.name)
|
||||
}
|
||||
}
|
||||
conn.close()
|
||||
@ -285,6 +299,10 @@ export function setupWSConnection (
|
||||
conn.binaryType = 'arraybuffer'
|
||||
// get doc, initialize if it does not exist yet
|
||||
const doc = getYDoc(documentId, token, gc, minio, initialContentId)
|
||||
if (doc === undefined) {
|
||||
conn.close()
|
||||
return
|
||||
}
|
||||
doc.conns.set(conn, { ids: new Set(), token })
|
||||
// listen and reply to events
|
||||
conn.on('message', (message: ArrayBuffer) => messageListener(conn, doc, new Uint8Array(message)))
|
||||
|
@ -273,12 +273,13 @@ export function start (
|
||||
const payload = decodeToken(token ?? '')
|
||||
console.log('client connected with payload', payload)
|
||||
|
||||
if (productId !== '' && payload.workspace.productId !== productId) {
|
||||
if (payload.workspace.productId !== productId) {
|
||||
throw new Error('Invalid workspace product')
|
||||
}
|
||||
|
||||
wss.handleUpgrade(request, socket, head, (ws) => wss.emit('connection', ws, request, payload))
|
||||
} catch (err) {
|
||||
console.error('invalid token', err)
|
||||
wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
const resp: Response<any> = {
|
||||
id: -1,
|
||||
|
Loading…
Reference in New Issue
Block a user