3
3
* sidebar.
4
4
*/
5
5
6
+ import { createSelector } from 'reselect' ;
7
+
8
+ import * as metadata from '../../helpers/annotation-metadata' ;
9
+ import { isSaved } from '../../helpers/annotation-metadata' ;
10
+ import { countIf , toTrueMap , trueKeys } from '../../util/collections' ;
11
+ import { createStoreModule , makeAction } from '../create-store' ;
12
+
13
+ import { routeModule } from './route' ;
14
+
6
15
/**
16
+ * @typedef {'anchored'|'orphan'|'timeout' } AnchorStatus
7
17
* @typedef {import('../../../types/api').Annotation } Annotation
8
18
* @typedef {import('../../../types/api').SavedAnnotation } SavedAnnotation
9
19
*/
15
25
* @prop {string } [$tag] - local-generated identifier
16
26
*/
17
27
18
- import { createSelector } from 'reselect' ;
19
-
20
- import * as metadata from '../../helpers/annotation-metadata' ;
21
- import { isSaved } from '../../helpers/annotation-metadata' ;
22
- import { countIf , toTrueMap , trueKeys } from '../../util/collections' ;
23
- import * as util from '../util' ;
24
- import { createStoreModule } from '../create-store' ;
25
-
26
- import { routeModule } from './route' ;
27
-
28
28
/**
29
29
* Return a copy of `current` with all matching annotations in `annotations`
30
30
* removed (matched on identifier—`id` or `$tag`)
@@ -36,19 +36,19 @@ import { routeModule } from './route';
36
36
* @param {AnnotationStub[] } annotations
37
37
*/
38
38
function excludeAnnotations ( current , annotations ) {
39
- const ids = { } ;
40
- const tags = { } ;
41
- annotations . forEach ( annot => {
39
+ const ids = new Set ( ) ;
40
+ const tags = new Set ( ) ;
41
+ for ( let annot of annotations ) {
42
42
if ( annot . id ) {
43
- ids [ annot . id ] = true ;
43
+ ids . add ( annot . id ) ;
44
44
}
45
45
if ( annot . $tag ) {
46
- tags [ annot . $tag ] = true ;
46
+ tags . add ( annot . $tag ) ;
47
47
}
48
- } ) ;
48
+ }
49
49
return current . filter ( annot => {
50
50
const shouldRemove =
51
- ( annot . id && annot . id in ids ) || ( annot . $tag && annot . $tag in tags ) ;
51
+ ( annot . id && ids . has ( annot . id ) ) || ( annot . $tag && tags . has ( annot . $tag ) ) ;
52
52
return ! shouldRemove ;
53
53
} ) ;
54
54
}
@@ -125,17 +125,20 @@ const initialState = {
125
125
/** @typedef {typeof initialState } State */
126
126
127
127
const reducers = {
128
- /** @param {State } state */
128
+ /**
129
+ * @param {State } state
130
+ * @param {{ annotations: Annotation[], currentAnnotationCount: number } } action
131
+ */
129
132
ADD_ANNOTATIONS ( state , action ) {
130
- const updatedIDs = { } ;
131
- const updatedTags = { } ;
133
+ const updatedIDs = new Set ( ) ;
134
+ const updatedTags = new Set ( ) ;
132
135
133
136
const added = [ ] ;
134
137
const unchanged = [ ] ;
135
138
const updated = [ ] ;
136
139
let nextTag = state . nextTag ;
137
140
138
- action . annotations . forEach ( annot => {
141
+ for ( let annot of action . annotations ) {
139
142
let existing ;
140
143
if ( annot . id ) {
141
144
existing = findByID ( state . annotations , annot . id ) ;
@@ -149,22 +152,22 @@ const reducers = {
149
152
// annotation
150
153
updated . push ( Object . assign ( { } , existing , annot ) ) ;
151
154
if ( annot . id ) {
152
- updatedIDs [ annot . id ] = true ;
155
+ updatedIDs . add ( annot . id ) ;
153
156
}
154
157
if ( existing . $tag ) {
155
- updatedTags [ existing . $tag ] = true ;
158
+ updatedTags . add ( existing . $tag ) ;
156
159
}
157
160
} else {
158
161
added . push ( initializeAnnotation ( annot , 't' + nextTag ) ) ;
159
162
++ nextTag ;
160
163
}
161
- } ) ;
164
+ }
162
165
163
- state . annotations . forEach ( annot => {
164
- if ( ! updatedIDs [ annot . id ] && ! updatedTags [ annot . $tag ] ) {
166
+ for ( let annot of state . annotations ) {
167
+ if ( ! updatedIDs . has ( annot . id ) && ! updatedTags . has ( annot . $tag ) ) {
165
168
unchanged . push ( annot ) ;
166
169
}
167
- } ) ;
170
+ }
168
171
169
172
return {
170
173
annotations : added . concat ( updated ) . concat ( unchanged ) ,
@@ -176,12 +179,18 @@ const reducers = {
176
179
return { annotations : [ ] , focused : { } , highlighted : { } } ;
177
180
} ,
178
181
179
- /** @param {State } state */
182
+ /**
183
+ * @param {State } state
184
+ * @param {{ focusedTags: string[] } } action
185
+ */
180
186
FOCUS_ANNOTATIONS ( state , action ) {
181
187
return { focused : toTrueMap ( action . focusedTags ) } ;
182
188
} ,
183
189
184
- /** @param {State } state */
190
+ /**
191
+ * @param {State } state
192
+ * @param {{ id: string } } action
193
+ */
185
194
HIDE_ANNOTATION ( state , action ) {
186
195
const anns = state . annotations . map ( ann => {
187
196
if ( ann . id !== action . id ) {
@@ -192,19 +201,28 @@ const reducers = {
192
201
return { annotations : anns } ;
193
202
} ,
194
203
195
- /** @param {State } state */
204
+ /**
205
+ * @param {State } state
206
+ * @param {{ highlighted: Record<string, boolean> } } action
207
+ */
196
208
HIGHLIGHT_ANNOTATIONS ( state , action ) {
197
209
return { highlighted : action . highlighted } ;
198
210
} ,
199
211
200
- /** @param {State } state */
212
+ /**
213
+ * @param {State } state
214
+ * @param {{ annotationsToRemove: AnnotationStub[], remainingAnnotations: Annotation[] } } action
215
+ */
201
216
REMOVE_ANNOTATIONS ( state , action ) {
202
217
return {
203
218
annotations : [ ...action . remainingAnnotations ] ,
204
219
} ;
205
220
} ,
206
221
207
- /** @param {State } state */
222
+ /**
223
+ * @param {State } state
224
+ * @param {{ id: string } } action
225
+ */
208
226
UNHIDE_ANNOTATION ( state , action ) {
209
227
const anns = state . annotations . map ( ann => {
210
228
if ( ann . id !== action . id ) {
@@ -215,7 +233,10 @@ const reducers = {
215
233
return { annotations : anns } ;
216
234
} ,
217
235
218
- /** @param {State } state */
236
+ /**
237
+ * @param {State } state
238
+ * @param {{ statusUpdates: Record<string, AnchorStatus> } } action
239
+ */
219
240
UPDATE_ANCHOR_STATUS ( state , action ) {
220
241
const annotations = state . annotations . map ( annot => {
221
242
if ( ! action . statusUpdates . hasOwnProperty ( annot . $tag ) ) {
@@ -232,7 +253,10 @@ const reducers = {
232
253
return { annotations } ;
233
254
} ,
234
255
235
- /** @param {State } state */
256
+ /**
257
+ * @param {State } state
258
+ * @param {{ id: string, isFlagged: boolean } } action
259
+ */
236
260
UPDATE_FLAG_STATUS ( state , action ) {
237
261
const annotations = state . annotations . map ( annot => {
238
262
const match = annot . id && annot . id === action . id ;
@@ -260,8 +284,6 @@ const reducers = {
260
284
} ,
261
285
} ;
262
286
263
- const actions = util . actionTypes ( reducers ) ;
264
-
265
287
/* Action creators */
266
288
267
289
/**
@@ -270,18 +292,23 @@ const actions = util.actionTypes(reducers);
270
292
* @param {Annotation[] } annotations - Array of annotation objects to add.
271
293
*/
272
294
function addAnnotations ( annotations ) {
295
+ /**
296
+ * @param {import('redux').Dispatch } dispatch
297
+ * @param {() => { annotations: State, route: import('./route').State } } getState
298
+ */
273
299
return function ( dispatch , getState ) {
274
300
const added = annotations . filter ( annot => {
275
301
return (
276
302
! annot . id || ! findByID ( getState ( ) . annotations . annotations , annot . id )
277
303
) ;
278
304
} ) ;
279
305
280
- dispatch ( {
281
- type : actions . ADD_ANNOTATIONS ,
282
- annotations,
283
- currentAnnotationCount : getState ( ) . annotations . annotations . length ,
284
- } ) ;
306
+ dispatch (
307
+ makeAction ( reducers , 'ADD_ANNOTATIONS' , {
308
+ annotations,
309
+ currentAnnotationCount : getState ( ) . annotations . annotations . length ,
310
+ } )
311
+ ) ;
285
312
286
313
// If we're not in the sidebar, we're done here.
287
314
// FIXME Split the annotation-adding from the anchoring code; possibly
@@ -322,7 +349,7 @@ function addAnnotations(annotations) {
322
349
323
350
/** Set the currently displayed annotations to the empty set. */
324
351
function clearAnnotations ( ) {
325
- return { type : actions . CLEAR_ANNOTATIONS } ;
352
+ return makeAction ( reducers , ' CLEAR_ANNOTATIONS' , undefined ) ;
326
353
}
327
354
328
355
/**
@@ -333,10 +360,7 @@ function clearAnnotations() {
333
360
* @param {string[] } tags - Identifiers of annotations to focus
334
361
*/
335
362
function focusAnnotations ( tags ) {
336
- return {
337
- type : actions . FOCUS_ANNOTATIONS ,
338
- focusedTags : tags ,
339
- } ;
363
+ return makeAction ( reducers , 'FOCUS_ANNOTATIONS' , { focusedTags : tags } ) ;
340
364
}
341
365
342
366
/**
@@ -348,10 +372,7 @@ function focusAnnotations(tags) {
348
372
* @param {string } id
349
373
*/
350
374
function hideAnnotation ( id ) {
351
- return {
352
- type : actions . HIDE_ANNOTATION ,
353
- id,
354
- } ;
375
+ return makeAction ( reducers , 'HIDE_ANNOTATION' , { id } ) ;
355
376
}
356
377
357
378
/**
@@ -365,10 +386,9 @@ function hideAnnotation(id) {
365
386
* @param {string[] } ids - annotations to highlight
366
387
*/
367
388
function highlightAnnotations ( ids ) {
368
- return {
369
- type : actions . HIGHLIGHT_ANNOTATIONS ,
389
+ return makeAction ( reducers , 'HIGHLIGHT_ANNOTATIONS' , {
370
390
highlighted : toTrueMap ( ids ) ,
371
- } ;
391
+ } ) ;
372
392
}
373
393
374
394
/**
@@ -379,16 +399,21 @@ function highlightAnnotations(ids) {
379
399
* only contain an `id` property.
380
400
*/
381
401
export function removeAnnotations ( annotations ) {
402
+ /**
403
+ * @param {import('redux').Dispatch } dispatch
404
+ * @param {() => { annotations: State } } getState
405
+ */
382
406
return ( dispatch , getState ) => {
383
407
const remainingAnnotations = excludeAnnotations (
384
408
getState ( ) . annotations . annotations ,
385
409
annotations
386
410
) ;
387
- dispatch ( {
388
- type : actions . REMOVE_ANNOTATIONS ,
389
- annotationsToRemove : annotations ,
390
- remainingAnnotations,
391
- } ) ;
411
+ dispatch (
412
+ makeAction ( reducers , 'REMOVE_ANNOTATIONS' , {
413
+ annotationsToRemove : annotations ,
414
+ remainingAnnotations,
415
+ } )
416
+ ) ;
392
417
} ;
393
418
}
394
419
@@ -401,23 +426,16 @@ export function removeAnnotations(annotations) {
401
426
* @param {string } id
402
427
*/
403
428
function unhideAnnotation ( id ) {
404
- return {
405
- type : actions . UNHIDE_ANNOTATION ,
406
- id,
407
- } ;
429
+ return makeAction ( reducers , 'UNHIDE_ANNOTATION' , { id } ) ;
408
430
}
409
431
410
432
/**
411
433
* Update the anchoring status of an annotation
412
434
*
413
- * @param {{ [tag: string]: 'anchored'|'orphan'|'timeout'} } statusUpdates - A
414
- * map of annotation tag to orphan status
435
+ * @param {Record<string, AnchorStatus> } statusUpdates - Map of annotation tag to orphan status
415
436
*/
416
437
function updateAnchorStatus ( statusUpdates ) {
417
- return {
418
- type : actions . UPDATE_ANCHOR_STATUS ,
419
- statusUpdates,
420
- } ;
438
+ return makeAction ( reducers , 'UPDATE_ANCHOR_STATUS' , { statusUpdates } ) ;
421
439
}
422
440
423
441
/**
@@ -429,11 +447,7 @@ function updateAnchorStatus(statusUpdates) {
429
447
*
430
448
*/
431
449
function updateFlagStatus ( id , isFlagged ) {
432
- return {
433
- type : actions . UPDATE_FLAG_STATUS ,
434
- id,
435
- isFlagged,
436
- } ;
450
+ return makeAction ( reducers , 'UPDATE_FLAG_STATUS' , { id, isFlagged } ) ;
437
451
}
438
452
439
453
/* Selectors */
@@ -487,12 +501,12 @@ function findAnnotationByID(state, id) {
487
501
*/
488
502
function findIDsForTags ( state , tags ) {
489
503
const ids = [ ] ;
490
- tags . forEach ( tag => {
504
+ for ( let tag of tags ) {
491
505
const annot = findByTag ( state . annotations , tag ) ;
492
506
if ( annot && annot . id ) {
493
507
ids . push ( annot . id ) ;
494
508
}
495
- } ) ;
509
+ }
496
510
return ids ;
497
511
}
498
512
0 commit comments