Use the new storage feature for the CollaborationCursor extension (#2096)

* extension-collaboration-cursor: make use of the new storage feature, deprecate users command and onUsers callback, clean up

* docs: update the jobs page
This commit is contained in:
Hans Pagel 2021-10-28 10:00:16 +02:00 committed by GitHub
parent 85cec0d4da
commit b004f1e21d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 38 deletions

View File

@ -65,7 +65,6 @@ const getInitialUser = () => {
export default () => {
const [status, setStatus] = useState('connecting')
const [users, setUsers] = useState([])
const [currentUser, setCurrentUser] = useState(getInitialUser)
const editor = useEditor({
@ -84,9 +83,6 @@ export default () => {
}),
CollaborationCursor.configure({
provider: websocketProvider,
onUpdate: updatedUsers => {
setUsers(updatedUsers)
},
}),
],
})
@ -96,7 +92,7 @@ export default () => {
const indexeddbProvider = new IndexeddbPersistence(room, ydoc)
indexeddbProvider.on('synced', () => {
console.log('Loaded content from database …')
console.log('Loaded content from database …')
})
// Update status changes
@ -109,7 +105,7 @@ export default () => {
useEffect(() => {
if (editor && currentUser) {
localStorage.setItem('currentUser', JSON.stringify(currentUser))
editor.chain().focus().user(currentUser).run()
editor.chain().focus().updateUser(currentUser).run()
}
}, [editor, currentUser])
@ -128,7 +124,7 @@ export default () => {
<div className="editor__footer">
<div className={`editor__status editor__status--${status}`}>
{status === 'connected'
? `${users.length} user${users.length === 1 ? '' : 's'} online in ${room}`
? `${editor.storage.collaborationCursor.users.length} user${editor.storage.collaborationCursor.users.length === 1 ? '' : 's'} online in ${room}`
: 'offline'}
</div>
<div className="editor__name">

View File

@ -5,7 +5,7 @@
<div class="editor__footer">
<div :class="`editor__status editor__status--${status}`">
<template v-if="status === 'connected'">
{{ users.length }} user{{ users.length === 1 ? '' : 's' }} online in {{ room }}
{{ editor.storage.collaborationCursor.users.length }} user{{ editor.storage.collaborationCursor.users.length === 1 ? '' : 's' }} online in {{ room }}
</template>
<template v-else>
offline
@ -61,7 +61,6 @@ export default {
provider: null,
indexdb: null,
editor: null,
users: [],
status: 'connecting',
room: getRandomRoom(),
}
@ -74,8 +73,6 @@ export default {
this.status = event.status
})
window.ydoc = ydoc
this.indexdb = new IndexeddbPersistence(this.room, ydoc)
this.editor = new Editor({
@ -92,9 +89,6 @@ export default {
CollaborationCursor.configure({
provider: this.provider,
user: this.currentUser,
onUpdate: users => {
this.users = users
},
}),
CharacterCount.configure({
limit: 10000,
@ -120,7 +114,7 @@ export default {
updateCurrentUser(attributes) {
this.currentUser = { ...this.currentUser, ...attributes }
this.editor.chain().focus().user(this.currentUser).run()
this.editor.chain().focus().updateUser(this.currentUser).run()
localStorage.setItem('currentUser', JSON.stringify(this.currentUser))
},

View File

@ -43,11 +43,15 @@ A render function for the cursor, look at [the extension source code](https://gi
## Commands
### user()
An object with the attributes of the current user. It expects a `name` and a `color`, but you can add additional fields, too.
### updateUser()
Pass an object with updated attributes of the current user. It expects a `name` and a `color`, but you can add additional fields, too.
```js
editor.commands.user({ name: 'Philipp Kühn', color: '#000000' })
editor.commands.updateUser({
name: 'Philipp Kühn',
color: '#000000',
avatar: 'https://unavatar.io/github/philippkuehn',
})
```
## Source code

View File

@ -1,13 +1,13 @@
# Jobs
Some great companies are looking for developers right now. If youre looking for a job to work with Tiptap and/or Hocuspocus, consider applying:
You enjoy to work with Tiptap? You are not alone. Some amazing companies are looking for developers with some Tiptap and/or [Hocuspocus](https://hocuspocus.dev) experience.
**[Software Engineer](https://gamma.app/docs/Software-Engineer-6s0e0grm9zk9w5s) @ Gamma**<br>
- **[Software Engineer](https://gamma.app/docs/Software-Engineer-6s0e0grm9zk9w5s) @ Gamma**<br>
React · Tiptap · San Francisco, CA
**[Frontend Engineer](https://birdeatsbug.recruitee.com/o/frontend-engineer-vuejs-graphql-browser-extension) @ Bird Eats Bug**<br>
- **[Javascript Engineer, Software Engineer, …](https://birdeatsbug.recruitee.com/) @ Bird Eats Bug**<br>
Vue.js · Tiptap · Remote · Berlin, Germany
**[Frontend Developer](https://bitcrowd.net/jobs) @ bitcrowd**<br>
- **[Frontend Developer](https://bitcrowd.net/jobs) @ bitcrowd**<br>
Tiptap · Remote · Berlin, Germany
Is your company hiring, too? We have more than 200,000 page views/month from people who love to work with Tiptap. Maybe we can find someone for you. Reach out to [humans@tiptap.dev](mailto:humans@tiptap.dev) with a link to your job description!
Is your company hiring, too? We have [200,000 page views/month](https://plausible.io/tiptap.dev?period=30d) from people who love to work with Tiptap. Maybe we can help you find the right person. Reach out to [humans@tiptap.dev](mailto:humans@tiptap.dev) with a link to your job description!

View File

@ -1,10 +1,17 @@
import { Extension } from '@tiptap/core'
import { yCursorPlugin } from 'y-prosemirror'
type CollaborationCursorStorage = {
users: { clientId: number, [key: string]: any }[],
}
export interface CollaborationCursorOptions {
provider: any,
user: Record<string, any>,
render (user: Record<string, any>): HTMLElement,
/**
* @deprecated The "onUpdate" option is deprecated. Please use `editor.storage.collaborationCursor.users` instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor
*/
onUpdate: (users: { clientId: number, [key: string]: any }[]) => null,
}
@ -14,6 +21,12 @@ declare module '@tiptap/core' {
/**
* Update details of the current user
*/
updateUser: (attributes: Record<string, any>) => ReturnType,
/**
* Update details of the current user
*
* @deprecated The "user" command is deprecated. Please use "updateUser" instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor
*/
user: (attributes: Record<string, any>) => ReturnType,
}
}
@ -28,7 +41,9 @@ const awarenessStatesToArray = (states: Map<number, Record<string, any>>) => {
})
}
export const CollaborationCursor = Extension.create<CollaborationCursorOptions>({
const defaultOnUpdate = () => null
export const CollaborationCursor = Extension.create<CollaborationCursorOptions, CollaborationCursorStorage>({
name: 'collaborationCursor',
addOptions() {
@ -51,19 +66,36 @@ export const CollaborationCursor = Extension.create<CollaborationCursorOptions>(
return cursor
},
onUpdate: () => null,
onUpdate: defaultOnUpdate,
}
},
onCreate() {
if (this.options.onUpdate !== defaultOnUpdate) {
console.warn('[tiptap warn]: DEPRECATED: The "onUpdate" option is deprecated. Please use `editor.storage.collaborationCursor.users` instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor')
}
},
addStorage() {
return {
users: [],
}
},
addCommands() {
return {
user: attributes => () => {
updateUser: attributes => () => {
this.options.user = attributes
this.options.provider.awareness.setLocalStateField('user', this.options.user)
return true
},
user: attributes => ({ editor }) => {
console.warn('[tiptap warn]: DEPRECATED: The "user" command is deprecated. Please use "updateUser" instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor')
return editor.commands.updateUser(attributes)
},
}
},
@ -72,22 +104,12 @@ export const CollaborationCursor = Extension.create<CollaborationCursorOptions>(
yCursorPlugin((() => {
this.options.provider.awareness.setLocalStateField('user', this.options.user)
this.options.provider.awareness.on('change', () => {
this.options.onUpdate(awarenessStatesToArray(this.options.provider.awareness.states))
})
this.storage.users = awarenessStatesToArray(this.options.provider.awareness.states)
this.options.provider.awareness.on('update', () => {
this.options.onUpdate(awarenessStatesToArray(this.options.provider.awareness.states))
this.storage.users = awarenessStatesToArray(this.options.provider.awareness.states)
})
this.options.provider.on('status', (event: { status: string }) => {
if (event.status === 'connected') {
this.options.provider.awareness.setLocalStateField('user', this.options.user)
}
})
this.options.onUpdate(awarenessStatesToArray(this.options.provider.awareness.states))
return this.options.provider.awareness
})(),
// @ts-ignore