@@ -82,8 +82,8 @@ import { getCurrentUser } from '@nextcloud/auth'
8282import { loadState } from ' @nextcloud/initial-state'
8383import { emit , subscribe , unsubscribe } from ' @nextcloud/event-bus'
8484import { Collaboration } from ' @tiptap/extension-collaboration'
85- import { CollaborationCursor } from ' @tiptap/extension-collaboration-cursor'
8685import { Doc } from ' yjs'
86+ import debounce from ' debounce'
8787
8888import {
8989 EDITOR ,
@@ -98,7 +98,7 @@ import {
9898import ReadonlyBar from ' ./Menu/ReadonlyBar.vue'
9999
100100import { logger } from ' ../helpers/logger.js'
101- import { getDocumentState , applyDocumentState } from ' ../helpers/yjs.js'
101+ import { yjsMessageTypes , getDocumentState , applyDocumentState , getMessageType , getAwarenessMessageClientIds } from ' ../helpers/yjs.js'
102102import { SyncService , ERROR_TYPE , IDLE_TIMEOUT } from ' ./../services/SyncService.js'
103103import createSyncServiceProvider from ' ./../services/SyncServiceProvider.js'
104104import AttachmentResolver from ' ./../services/AttachmentResolver.js'
@@ -107,7 +107,7 @@ import { createEditor, serializePlainText, loadSyntaxHighlight } from './../Edit
107107import { createMarkdownSerializer } from ' ./../extensions/Markdown.js'
108108import markdownit from ' ./../markdownit/index.js'
109109
110- import { Keymap } from ' ./ ../extensions/index.js'
110+ import { CollaborationCursor , Keymap } from ' ../extensions/index.js'
111111import DocumentStatus from ' ./Editor/DocumentStatus.vue'
112112import isMobile from ' ./../mixins/isMobile.js'
113113import setContent from ' ./../mixins/setContent.js'
@@ -321,10 +321,12 @@ export default {
321321 },
322322 created () {
323323 this .$ydoc = new Doc ()
324+ this .$ydoc .on (' update' , this .onYjsUpdate )
324325 this .$providers = []
325326 this .$editor = null
326327 this .$syncService = null
327328 this .$attachmentResolver = null
329+ this .$cursorLabelTimeouts = {}
328330 },
329331 beforeDestroy () {
330332 if (! this .richWorkspace ) {
@@ -642,6 +644,35 @@ export default {
642644 this .emit (' delete-image-node' , imageUrl)
643645 },
644646
647+ onYjsUpdate (update , _origin , _doc , _tr ) {
648+ // Update cursor labels for clients from awareness message
649+ if (getMessageType (update) === yjsMessageTypes .awareness ) {
650+ if (! this .$editor ) {
651+ return
652+ }
653+
654+ const clientIds = getAwarenessMessageClientIds (update)
655+ const updateUsers = this .$editor .storage .collaborationCursor .users
656+ .filter (u => clientIds .includes (u .clientId ))
657+ for (const user of updateUsers) {
658+ this .updateCursorLabel (user)
659+ }
660+ }
661+ },
662+
663+ updateCursorLabel: debounce (function (user ) {
664+ // Remove CSS class that fades out and hides the cursor label and bring it back after five seconds.
665+ // Limit this to once every second to prevent it from happening with each awareness update.
666+ const cursorLabelEls = document .getElementsByClassName (` collaboration-cursor__label__${ user .name } ` )
667+ for (const el of cursorLabelEls) {
668+ el .classList .remove (' collaboration-cursor__label_fadeout' )
669+ clearTimeout (this .$cursorLabelTimeouts [user .clientId ])
670+ this .$cursorLabelTimeouts [user .clientId ] = setTimeout (() => {
671+ el .classList .add (' collaboration-cursor__label_fadeout' )
672+ }, 5000 )
673+ }
674+ }, 1000 , true ),
675+
645676 async close () {
646677 if (this .currentSession && this .$syncService ) {
647678 try {
@@ -782,7 +813,6 @@ export default {
782813 width: 100 % ;
783814 background- color: var (-- color- main- background);
784815}
785-
786816< / style>
787817
788818< style lang= " scss" >
@@ -914,4 +944,13 @@ export default {
914944 white- space: nowrap;
915945 }
916946
947+ .collaboration - cursor__label_fadeout {
948+ // transition: visibility 0s 1s, opacity 1s linear;
949+ animation: fade- out- label var (-- animation- slow) forwards;
950+ }
951+
952+ @keyframes fade- out- label {
953+ 0 % { opacity: 1 ; }
954+ 100 % { opacity: 0 ; }
955+ }
917956< / style>
0 commit comments