1 import { Awareness } from 'y-protocols/awareness'
2 import type { UserState } from '@lexical/yjs'
4 export type DocsUserState = UserState & {
5 // eslint-disable-next-line @typescript-eslint/no-explicit-any
6 awarenessData: Record<string, any>
7 // eslint-disable-next-line @typescript-eslint/no-explicit-any
11 export class DocsAwareness extends Awareness {
12 getLocalState(): DocsUserState | null {
13 return super.getLocalState() as DocsUserState
16 getStates(): Map<number, DocsUserState> {
17 return super.getStates() as Map<number, DocsUserState>
21 * When a single client refreshes their page, they may not have the chance to propagate their own removal
22 * This means that when the user comes back to the page after the refresh, they will see their old user avatar.
23 * (Other users will also see the old avatar). This function looks for duplicates based on the username (email)
24 * and if there are more than one state entries for an email, it will delete the one that has the older lastUpdated.
26 removeDuplicateClients(): void {
27 const states = this.getStates()
29 const alreadyBrowsedEntries: Map<string, number> = new Map()
31 for (const [clientId, userState] of states) {
32 const username = userState.name
34 if (alreadyBrowsedEntries.has(username)) {
35 const previousClientId = alreadyBrowsedEntries.get(username) as number
37 const currentLastUpdated = this.meta.get(clientId)?.lastUpdated ?? 0
38 const previousLastUpdated = this.meta.get(previousClientId)?.lastUpdated ?? 0
40 if (currentLastUpdated > previousLastUpdated) {
41 this.states.delete(previousClientId)
42 } else if (currentLastUpdated < previousLastUpdated) {
43 this.states.delete(clientId)
47 alreadyBrowsedEntries.set(username, clientId)
51 getClientIds(): number[] {
52 return Array.from(this.getStates().keys())