1
- import { ExcalidrawElement } from '../types' ;
2
1
import { arrayToMap } from './array.to.map' ;
2
+ import { ExcalidrawElement } from '../../excalidraw/types' ;
3
+ import { arrayToMapBy } from './array.to.map.by' ;
4
+ import { Logger } from '@nestjs/common' ;
3
5
// import { orderByFractionalIndex, syncInvalidIndices } from './fractionalIndex';
4
6
5
7
const shouldDiscardRemoteElement = (
@@ -48,6 +50,8 @@ const shouldDiscardRemoteElement = (
48
50
{ leading: true, trailing: false },
49
51
);*/
50
52
53
+ const logger = new Logger ( 'reconcileElements' ) ;
54
+
51
55
export const reconcileElements = (
52
56
localElements : readonly ExcalidrawElement [ ] ,
53
57
remoteElements : readonly ExcalidrawElement [ ] ,
@@ -83,8 +87,6 @@ export const reconcileElements = (
83
87
}
84
88
}
85
89
86
- // const orderedElements = orderByFractionalIndex(reconciledElements);
87
-
88
90
/**
89
91
* todo: not sure how important is this and how does it affect the end result
90
92
* since the debounce is set to 60 seconds, which might mean that the room has already closed
@@ -94,7 +96,53 @@ export const reconcileElements = (
94
96
95
97
// de-duplicate indices
96
98
// const syncedElemented = syncInvalidIndices(orderedElements);
99
+ try {
100
+ return orderByPrecedingElement ( reconciledElements ) ;
101
+ } catch ( error ) {
102
+ logger . error ( error . message ) ;
103
+ return reconciledElements ;
104
+ }
105
+ } ;
106
+
107
+ const orderByPrecedingElement = (
108
+ unOrderedElements : ExcalidrawElement [ ] ,
109
+ ) : ExcalidrawElement [ ] | never => {
110
+ // for zero or one element return the same array, as it's already sorted
111
+ if ( unOrderedElements . length < 2 ) {
112
+ return unOrderedElements ;
113
+ }
114
+ // validated there is just one starting element
115
+ const startElements = unOrderedElements . filter (
116
+ ( el ) => el . __precedingElement__ === '^' ,
117
+ ) ;
118
+
119
+ if ( startElements . length !== 1 ) {
120
+ throw new Error (
121
+ `There must be exactly one element with __precedingElement__ = '^'` ,
122
+ ) ;
123
+ }
124
+ // create a map of elements by <__precedingElement__, element that has this __precedingElement__ value>
125
+ // for easy access
126
+ const elementMapByPrecedingKey = arrayToMapBy (
127
+ unOrderedElements ,
128
+ '__precedingElement__' ,
129
+ ) ;
130
+ const orderedElements : ExcalidrawElement [ ] = [ ] ;
131
+ // the array is starting with element that has no preceding element
132
+ let parentElement = startElements [ 0 ] ;
133
+ orderedElements . push ( parentElement ) ;
134
+ // Follow the chain of __precedingElement__
135
+ while ( true ) {
136
+ const childElement = elementMapByPrecedingKey . get ( parentElement . id ) ;
137
+
138
+ if ( ! childElement ) {
139
+ // we have reached the end of the chain
140
+ break ;
141
+ }
142
+
143
+ orderedElements . push ( childElement ) ;
144
+ parentElement = childElement ;
145
+ }
97
146
98
- // return orderedElements as ReconciledExcalidrawElement[];
99
- return reconciledElements ;
147
+ return orderedElements ;
100
148
} ;
0 commit comments