Skip to content

Commit 13914b8

Browse files
committed
Add missing types to annotation and route store modules
- Add state and action types to reducers - Replace Array.forEach calls with for..of loops to enable better type inference. - Replace plain objects used as sets with ES sets
1 parent 31ac4c8 commit 13914b8

File tree

2 files changed

+102
-86
lines changed

2 files changed

+102
-86
lines changed

src/sidebar/store/modules/annotations.js

Lines changed: 89 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@
33
* sidebar.
44
*/
55

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+
615
/**
16+
* @typedef {'anchored'|'orphan'|'timeout'} AnchorStatus
717
* @typedef {import('../../../types/api').Annotation} Annotation
818
* @typedef {import('../../../types/api').SavedAnnotation} SavedAnnotation
919
*/
@@ -15,16 +25,6 @@
1525
* @prop {string} [$tag] - local-generated identifier
1626
*/
1727

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-
2828
/**
2929
* Return a copy of `current` with all matching annotations in `annotations`
3030
* removed (matched on identifier—`id` or `$tag`)
@@ -36,19 +36,19 @@ import { routeModule } from './route';
3636
* @param {AnnotationStub[]} annotations
3737
*/
3838
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) {
4242
if (annot.id) {
43-
ids[annot.id] = true;
43+
ids.add(annot.id);
4444
}
4545
if (annot.$tag) {
46-
tags[annot.$tag] = true;
46+
tags.add(annot.$tag);
4747
}
48-
});
48+
}
4949
return current.filter(annot => {
5050
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));
5252
return !shouldRemove;
5353
});
5454
}
@@ -125,17 +125,20 @@ const initialState = {
125125
/** @typedef {typeof initialState} State */
126126

127127
const reducers = {
128-
/** @param {State} state */
128+
/**
129+
* @param {State} state
130+
* @param {{ annotations: Annotation[], currentAnnotationCount: number }} action
131+
*/
129132
ADD_ANNOTATIONS(state, action) {
130-
const updatedIDs = {};
131-
const updatedTags = {};
133+
const updatedIDs = new Set();
134+
const updatedTags = new Set();
132135

133136
const added = [];
134137
const unchanged = [];
135138
const updated = [];
136139
let nextTag = state.nextTag;
137140

138-
action.annotations.forEach(annot => {
141+
for (let annot of action.annotations) {
139142
let existing;
140143
if (annot.id) {
141144
existing = findByID(state.annotations, annot.id);
@@ -149,22 +152,22 @@ const reducers = {
149152
// annotation
150153
updated.push(Object.assign({}, existing, annot));
151154
if (annot.id) {
152-
updatedIDs[annot.id] = true;
155+
updatedIDs.add(annot.id);
153156
}
154157
if (existing.$tag) {
155-
updatedTags[existing.$tag] = true;
158+
updatedTags.add(existing.$tag);
156159
}
157160
} else {
158161
added.push(initializeAnnotation(annot, 't' + nextTag));
159162
++nextTag;
160163
}
161-
});
164+
}
162165

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)) {
165168
unchanged.push(annot);
166169
}
167-
});
170+
}
168171

169172
return {
170173
annotations: added.concat(updated).concat(unchanged),
@@ -176,12 +179,18 @@ const reducers = {
176179
return { annotations: [], focused: {}, highlighted: {} };
177180
},
178181

179-
/** @param {State} state */
182+
/**
183+
* @param {State} state
184+
* @param {{ focusedTags: string[] }} action
185+
*/
180186
FOCUS_ANNOTATIONS(state, action) {
181187
return { focused: toTrueMap(action.focusedTags) };
182188
},
183189

184-
/** @param {State} state */
190+
/**
191+
* @param {State} state
192+
* @param {{ id: string }} action
193+
*/
185194
HIDE_ANNOTATION(state, action) {
186195
const anns = state.annotations.map(ann => {
187196
if (ann.id !== action.id) {
@@ -192,19 +201,28 @@ const reducers = {
192201
return { annotations: anns };
193202
},
194203

195-
/** @param {State} state */
204+
/**
205+
* @param {State} state
206+
* @param {{ highlighted: Record<string, boolean> }} action
207+
*/
196208
HIGHLIGHT_ANNOTATIONS(state, action) {
197209
return { highlighted: action.highlighted };
198210
},
199211

200-
/** @param {State} state */
212+
/**
213+
* @param {State} state
214+
* @param {{ annotationsToRemove: AnnotationStub[], remainingAnnotations: Annotation[] }} action
215+
*/
201216
REMOVE_ANNOTATIONS(state, action) {
202217
return {
203218
annotations: [...action.remainingAnnotations],
204219
};
205220
},
206221

207-
/** @param {State} state */
222+
/**
223+
* @param {State} state
224+
* @param {{ id: string }} action
225+
*/
208226
UNHIDE_ANNOTATION(state, action) {
209227
const anns = state.annotations.map(ann => {
210228
if (ann.id !== action.id) {
@@ -215,7 +233,10 @@ const reducers = {
215233
return { annotations: anns };
216234
},
217235

218-
/** @param {State} state */
236+
/**
237+
* @param {State} state
238+
* @param {{ statusUpdates: Record<string, AnchorStatus> }} action
239+
*/
219240
UPDATE_ANCHOR_STATUS(state, action) {
220241
const annotations = state.annotations.map(annot => {
221242
if (!action.statusUpdates.hasOwnProperty(annot.$tag)) {
@@ -232,7 +253,10 @@ const reducers = {
232253
return { annotations };
233254
},
234255

235-
/** @param {State} state */
256+
/**
257+
* @param {State} state
258+
* @param {{ id: string, isFlagged: boolean }} action
259+
*/
236260
UPDATE_FLAG_STATUS(state, action) {
237261
const annotations = state.annotations.map(annot => {
238262
const match = annot.id && annot.id === action.id;
@@ -260,8 +284,6 @@ const reducers = {
260284
},
261285
};
262286

263-
const actions = util.actionTypes(reducers);
264-
265287
/* Action creators */
266288

267289
/**
@@ -270,18 +292,23 @@ const actions = util.actionTypes(reducers);
270292
* @param {Annotation[]} annotations - Array of annotation objects to add.
271293
*/
272294
function addAnnotations(annotations) {
295+
/**
296+
* @param {import('redux').Dispatch} dispatch
297+
* @param {() => { annotations: State, route: import('./route').State }} getState
298+
*/
273299
return function (dispatch, getState) {
274300
const added = annotations.filter(annot => {
275301
return (
276302
!annot.id || !findByID(getState().annotations.annotations, annot.id)
277303
);
278304
});
279305

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+
);
285312

286313
// If we're not in the sidebar, we're done here.
287314
// FIXME Split the annotation-adding from the anchoring code; possibly
@@ -322,7 +349,7 @@ function addAnnotations(annotations) {
322349

323350
/** Set the currently displayed annotations to the empty set. */
324351
function clearAnnotations() {
325-
return { type: actions.CLEAR_ANNOTATIONS };
352+
return makeAction(reducers, 'CLEAR_ANNOTATIONS', undefined);
326353
}
327354

328355
/**
@@ -333,10 +360,7 @@ function clearAnnotations() {
333360
* @param {string[]} tags - Identifiers of annotations to focus
334361
*/
335362
function focusAnnotations(tags) {
336-
return {
337-
type: actions.FOCUS_ANNOTATIONS,
338-
focusedTags: tags,
339-
};
363+
return makeAction(reducers, 'FOCUS_ANNOTATIONS', { focusedTags: tags });
340364
}
341365

342366
/**
@@ -348,10 +372,7 @@ function focusAnnotations(tags) {
348372
* @param {string} id
349373
*/
350374
function hideAnnotation(id) {
351-
return {
352-
type: actions.HIDE_ANNOTATION,
353-
id,
354-
};
375+
return makeAction(reducers, 'HIDE_ANNOTATION', { id });
355376
}
356377

357378
/**
@@ -365,10 +386,9 @@ function hideAnnotation(id) {
365386
* @param {string[]} ids - annotations to highlight
366387
*/
367388
function highlightAnnotations(ids) {
368-
return {
369-
type: actions.HIGHLIGHT_ANNOTATIONS,
389+
return makeAction(reducers, 'HIGHLIGHT_ANNOTATIONS', {
370390
highlighted: toTrueMap(ids),
371-
};
391+
});
372392
}
373393

374394
/**
@@ -379,16 +399,21 @@ function highlightAnnotations(ids) {
379399
* only contain an `id` property.
380400
*/
381401
export function removeAnnotations(annotations) {
402+
/**
403+
* @param {import('redux').Dispatch} dispatch
404+
* @param {() => { annotations: State }} getState
405+
*/
382406
return (dispatch, getState) => {
383407
const remainingAnnotations = excludeAnnotations(
384408
getState().annotations.annotations,
385409
annotations
386410
);
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+
);
392417
};
393418
}
394419

@@ -401,23 +426,16 @@ export function removeAnnotations(annotations) {
401426
* @param {string} id
402427
*/
403428
function unhideAnnotation(id) {
404-
return {
405-
type: actions.UNHIDE_ANNOTATION,
406-
id,
407-
};
429+
return makeAction(reducers, 'UNHIDE_ANNOTATION', { id });
408430
}
409431

410432
/**
411433
* Update the anchoring status of an annotation
412434
*
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
415436
*/
416437
function updateAnchorStatus(statusUpdates) {
417-
return {
418-
type: actions.UPDATE_ANCHOR_STATUS,
419-
statusUpdates,
420-
};
438+
return makeAction(reducers, 'UPDATE_ANCHOR_STATUS', { statusUpdates });
421439
}
422440

423441
/**
@@ -429,11 +447,7 @@ function updateAnchorStatus(statusUpdates) {
429447
*
430448
*/
431449
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 });
437451
}
438452

439453
/* Selectors */
@@ -487,12 +501,12 @@ function findAnnotationByID(state, id) {
487501
*/
488502
function findIDsForTags(state, tags) {
489503
const ids = [];
490-
tags.forEach(tag => {
504+
for (let tag of tags) {
491505
const annot = findByTag(state.annotations, tag);
492506
if (annot && annot.id) {
493507
ids.push(annot.id);
494508
}
495-
});
509+
}
496510
return ids;
497511
}
498512

0 commit comments

Comments
 (0)