Commit 987bcfa5 authored by Robert Knight's avatar Robert Knight

Infer store module types automatically

Remove the need to define the type of store created by each module
manually by adding a `StoreFromModule` helper in `create-store.js` which
can infer the store type from a store module configuration. Using this
the type of a store composed from several modules can then be created
with:

```
import fooModule from './modules/foo';
import barModule from './modules/bar';

// Define type of store returned by `createStore([fooModule, barModule])`
/** @typedef {StoreType<fooModule> & StoreType<barModule>} AppStore */
```

Ideally `createStore` would just infer the type based upon its
arguments. I haven't worked out how to do that yet. Nevertheless, this
still removes the need for a lot of manually defined types.

To ensure more useful error messages from TS if a store module's configuration
has the wrong shape a `storeModule` helper has been added. This wraps
the configuration for each module to check its shape before the
individual modules are combined into one type for the store. This helper could
also perform runtime validation in future.

 - Add `StoreFromModule` type in `create-store.js` and several helpers
   to support it
 - Modify each store module to wrap the export in `storeModule` and
   remove any manually defined store types
parent 43d1d1d1
...@@ -7,6 +7,85 @@ import immutable from '../util/immutable'; ...@@ -7,6 +7,85 @@ import immutable from '../util/immutable';
import { createReducer, bindSelectors } from './util'; import { createReducer, bindSelectors } from './util';
/**
* Helper that strips the first argument from a function type.
*
* @template F
* @typedef {F extends (x: any, ...args: infer P) => infer R ? (...args: P) => R : never} OmitFirstArg
*/
/**
* Helper that converts an object of selector functions, which take a `state`
* parameter plus zero or more arguments, into selector methods, with no `state` parameter.
*
* @template T
* @typedef {{ [K in keyof T]: OmitFirstArg<T[K]> }} SelectorMethods
*/
/**
* Map of action name to reducer function.
*
* @template State
* @typedef {{ [action: string]: (s: State, action: any) => Partial<State> }} Reducers
*/
/**
* Configuration for a store module.
*
* @template State
* @template {object} Actions
* @template {object} Selectors
* @template {object} RootSelectors
* @typedef Module
* @prop {(...args: any[]) => State} init -
* Function that returns the initial state for the module
* @prop {string} namespace -
* The key under which this module's state will live in the store's root state
* @prop {Reducers<State>} update -
* Map of action types to "reducer" functions that process an action and return
* the changes to the state
* @prop {Actions} actions
* Object containing action creator functions
* @prop {Selectors} selectors
* Object containing selector functions
* @prop {RootSelectors} [rootSelectors]
*/
/**
* Replace a type `T` with `Fallback` if `T` is `any`.
*
* Based on https://stackoverflow.com/a/61626123/434243.
*
* @template T
* @template Fallback
* @typedef {0 extends (1 & T) ? Fallback : T} DefaultIfAny
*/
/**
* Helper for getting the type of store produced by `createStore` when
* passed a given module.
*
* To get the type for a store created from several modules, use `&`:
*
* `StoreFromModule<firstModule> & StoreFromModule<secondModule>`
*
* @template T
* @typedef {T extends Module<any, infer Actions, infer Selectors, infer RootSelectors> ?
* Store<Actions,Selectors,DefaultIfAny<RootSelectors,{}>> : never} StoreFromModule
*/
/**
* Redux store augmented with methods to dispatch actions and select state.
*
* @template {object} Actions
* @template {object} Selectors
* @template {object} RootSelectors
* @typedef {redux.Store &
* Actions &
* SelectorMethods<Selectors> &
* SelectorMethods<RootSelectors>} Store
*/
/** /**
* Create a Redux store from a set of _modules_. * Create a Redux store from a set of _modules_.
* *
...@@ -22,9 +101,10 @@ import { createReducer, bindSelectors } from './util'; ...@@ -22,9 +101,10 @@ import { createReducer, bindSelectors } from './util';
* each action and selector from the input modules as a method which operates on * each action and selector from the input modules as a method which operates on
* the store. * the store.
* *
* @param {Object[]} modules * @param {Module<any,any,any,any>[]} modules
* @param {any[]} [initArgs] - Arguments to pass to each state module's `init` function * @param {any[]} [initArgs] - Arguments to pass to each state module's `init` function
* @param {any[]} [middleware] - List of additional Redux middlewares to use * @param {any[]} [middleware] - List of additional Redux middlewares to use
* @return Store<any,any,any>
*/ */
export default function createStore(modules, initArgs = [], middleware = []) { export default function createStore(modules, initArgs = [], middleware = []) {
// Create the initial state and state update function. // Create the initial state and state update function.
...@@ -90,3 +170,21 @@ export default function createStore(modules, initArgs = [], middleware = []) { ...@@ -90,3 +170,21 @@ export default function createStore(modules, initArgs = [], middleware = []) {
return store; return store;
} }
/**
* Helper to validate a store module configuration before it is passed to
* `createStore`.
*
* @template State
* @template Actions
* @template Selectors
* @template RootSelectors
* @param {Module<State,Actions,Selectors,RootSelectors>} config
* @return {Module<State,Actions,Selectors,RootSelectors>}
*/
export function storeModule(config) {
// This helper doesn't currently do anything at runtime. It does ensure more
// helpful error messages when typechecking if there is something incorrect
// in the configuration.
return config;
}
...@@ -49,46 +49,28 @@ import toastMessages from './modules/toast-messages'; ...@@ -49,46 +49,28 @@ import toastMessages from './modules/toast-messages';
import viewer from './modules/viewer'; import viewer from './modules/viewer';
/** /**
* // Base redux store * @template M
* @typedef {import("redux").Store} ReduxStore * @typedef {import('./create-store').StoreFromModule<M>} StoreFromModule
* */
* // Custom stores
* @typedef {import("./modules/activity").ActivityStore} ActivityStore /**
* @typedef {import("./modules/annotations").AnnotationsStore} AnnotationsStore * @typedef {StoreFromModule<activity> &
* @typedef {import("./modules/defaults").DefaultsStore} DefaultsStore * StoreFromModule<annotations> &
* @typedef {import("./modules/direct-linked").DirectLinkedStore} DirectLinkedStore * StoreFromModule<defaults> &
* @typedef {import("./modules/drafts").DraftsStore} DraftsStore * StoreFromModule<directLinked> &
* @typedef {import("./modules/filters").FiltersStore} FiltersStore * StoreFromModule<drafts> &
* @typedef {import("./modules/frames").FramesStore} FramesStore * StoreFromModule<filters> &
* @typedef {import("./modules/groups").GroupsStore} GroupsStore * StoreFromModule<frames> &
* @typedef {import("./modules/links").LinksStore} LinksStore * StoreFromModule<groups> &
* @typedef {import("./modules/real-time-updates").RealTimeUpdatesStore} RealTimeUpdatesStore * StoreFromModule<links> &
* @typedef {import("./modules/route").RouteStore} RouteStore * StoreFromModule<realTimeUpdates> &
* @typedef {import("./modules/selection").SelectionStore} SelectionStore * StoreFromModule<route> &
* @typedef {import("./modules/session").SessionStore} SessionStore * StoreFromModule<selection> &
* @typedef {import("./modules/sidebar-panels").SidebarPanelsStore} SidebarPanelsStore * StoreFromModule<session> &
* @typedef {import("./modules/toast-messages").ToastMessagesStore} ToastMessagesStore * StoreFromModule<sidebarPanels> &
* @typedef {import("./modules/viewer").ViewerStore} ViewerStore * StoreFromModule<toastMessages> &
* // TODO: add more stores * StoreFromModule<viewer>
* * } SidebarStore
* // Combine all stores
* @typedef {ReduxStore &
* ActivityStore &
* AnnotationsStore &
* DefaultsStore &
* DirectLinkedStore &
* DraftsStore &
* FiltersStore &
* FramesStore &
* GroupsStore &
* LinksStore &
* RealTimeUpdatesStore &
* RouteStore &
* SelectionStore &
* SessionStore &
* SidebarPanelsStore &
* ToastMessagesStore &
* ViewerStore} SidebarStore
*/ */
/** /**
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
import { actionTypes } from '../util'; import { actionTypes } from '../util';
import { storeModule } from '../create-store';
function init() { function init() {
return { return {
...@@ -169,25 +170,7 @@ function isSavingAnnotation(state, annotation) { ...@@ -169,25 +170,7 @@ function isSavingAnnotation(state, annotation) {
/** @typedef {import('../../../types/api').Annotation} Annotation */ /** @typedef {import('../../../types/api').Annotation} Annotation */
/** export default storeModule({
* @typedef ActivityStore
*
* // Actions
* @prop {typeof annotationFetchStarted} annotationFetchStarted
* @prop {typeof annotationFetchFinished} annotationFetchFinished
* @prop {typeof annotationSaveStarted} annotationSaveStarted
* @prop {typeof annotationSaveFinished} annotationSaveFinished
* @prop {typeof apiRequestStarted} apiRequestStarted
* @prop {typeof apiRequestFinished} apiRequestFinished
*
* // Selectors
* @prop {() => boolean} hasFetchedAnnotations
* @prop {() => boolean} isLoading
* @prop {() => boolean} isFetchingAnnotations
* @prop {(a: Annotation) => boolean} isSavingAnnotation
*/
export default {
init, init,
update, update,
namespace: 'activity', namespace: 'activity',
...@@ -207,4 +190,4 @@ export default { ...@@ -207,4 +190,4 @@ export default {
isFetchingAnnotations, isFetchingAnnotations,
isSavingAnnotation, isSavingAnnotation,
}, },
}; });
...@@ -17,6 +17,7 @@ import { createSelector } from 'reselect'; ...@@ -17,6 +17,7 @@ import { createSelector } from 'reselect';
import * as metadata from '../../util/annotation-metadata'; import * as metadata from '../../util/annotation-metadata';
import { countIf, toTrueMap, trueKeys } from '../../util/collections'; import { countIf, toTrueMap, trueKeys } from '../../util/collections';
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
import route from './route'; import route from './route';
...@@ -90,6 +91,7 @@ function initializeAnnotation(annotation, tag) { ...@@ -90,6 +91,7 @@ function initializeAnnotation(annotation, tag) {
function init() { function init() {
return { return {
/** @type {Annotation[]} */
annotations: [], annotations: [],
// A set of annotations that are currently "focused" — e.g. hovered over in // A set of annotations that are currently "focused" — e.g. hovered over in
// the UI // the UI
...@@ -344,7 +346,7 @@ function highlightAnnotations(ids) { ...@@ -344,7 +346,7 @@ function highlightAnnotations(ids) {
* Annotations to remove. These may be complete annotations or stubs which * Annotations to remove. These may be complete annotations or stubs which
* only contain an `id` property. * only contain an `id` property.
*/ */
function removeAnnotations(annotations) { export function removeAnnotations(annotations) {
return (dispatch, getState) => { return (dispatch, getState) => {
const remainingAnnotations = excludeAnnotations( const remainingAnnotations = excludeAnnotations(
getState().annotations.annotations, getState().annotations.annotations,
...@@ -552,39 +554,7 @@ function savedAnnotations(state) { ...@@ -552,39 +554,7 @@ function savedAnnotations(state) {
}); });
} }
/** export default storeModule({
* @typedef AnnotationsStore
*
* // Actions
* @prop {typeof addAnnotations} addAnnotations
* @prop {typeof clearAnnotations} clearAnnotations
* @prop {typeof focusAnnotations} focusAnnotations
* @prop {typeof hideAnnotation} hideAnnotation
* @prop {typeof highlightAnnotations} highlightAnnotations
* @prop {typeof removeAnnotations} removeAnnotations
* @prop {typeof unhideAnnotation} unhideAnnotation
* @prop {typeof updateAnchorStatus} updateAnchorStatus
* @prop {typeof updateFlagStatus} updateFlagStatus
*
* // Selectors
* @prop {() => Annotation[]} allAnnotations
* @prop {() => number} annotationCount
* @prop {(id: string) => boolean} annotationExists
* @prop {(id: string) => Annotation} findAnnotationByID
* @prop {(tags: string[]) => string[]} findIDsForTags
* @prop {() => string[]} focusedAnnotations
* @prop {() => string[]} highlightedAnnotations
* @prop {(tag: string) => boolean} isAnnotationFocused
* @prop {() => boolean} isWaitingToAnchorAnnotations
* @prop {() => Annotation[]} newAnnotations
* @prop {() => Annotation[]} newHighlights
* @prop {() => number} noteCount
* @prop {() => number} orphanCount
* @prop {() => Annotation[]} savedAnnotations
*/
export default {
init: init, init: init,
namespace: 'annotations', namespace: 'annotations',
update: update, update: update,
...@@ -599,7 +569,6 @@ export default { ...@@ -599,7 +569,6 @@ export default {
updateAnchorStatus, updateAnchorStatus,
updateFlagStatus, updateFlagStatus,
}, },
selectors: { selectors: {
allAnnotations, allAnnotations,
annotationCount, annotationCount,
...@@ -616,4 +585,4 @@ export default { ...@@ -616,4 +585,4 @@ export default {
orphanCount, orphanCount,
savedAnnotations, savedAnnotations,
}, },
}; });
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
/** /**
* A store module for managing client-side user-convenience defaults. * A store module for managing client-side user-convenience defaults.
* *
...@@ -55,18 +57,7 @@ function getDefaults(state) { ...@@ -55,18 +57,7 @@ function getDefaults(state) {
return state; return state;
} }
/** export default storeModule({
* @typedef DefaultsStore
*
* // Actions
* @prop {typeof setDefault} setDefault
*
* // Selectors
* @prop {(key: string) => string|null} getDefault
* @prop {() => Object.<string,string|null>} getDefaults
*/
export default {
init, init,
namespace: 'defaults', namespace: 'defaults',
update, update,
...@@ -77,4 +68,4 @@ export default { ...@@ -77,4 +68,4 @@ export default {
getDefault, getDefault,
getDefaults, getDefaults,
}, },
}; });
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
function init(settings) { function init(settings) {
return { return {
/** /**
...@@ -11,7 +13,7 @@ function init(settings) { ...@@ -11,7 +13,7 @@ function init(settings) {
* from the group or clears the selection, the direct link is "consumed" * from the group or clears the selection, the direct link is "consumed"
* and no longer used. * and no longer used.
* *
* @type {string} * @type {string|null}
*/ */
directLinkedGroupId: settings.group || null, directLinkedGroupId: settings.group || null,
...@@ -24,7 +26,7 @@ function init(settings) { ...@@ -24,7 +26,7 @@ function init(settings) {
* switches to a different group manually, the direct link is "consumed" * switches to a different group manually, the direct link is "consumed"
* and no longer used. * and no longer used.
* *
* @type {string} * @type {string|null}
*/ */
directLinkedAnnotationId: settings.annotations || null, directLinkedAnnotationId: settings.annotations || null,
...@@ -140,23 +142,7 @@ function directLinkedGroupFetchFailed(state) { ...@@ -140,23 +142,7 @@ function directLinkedGroupFetchFailed(state) {
return state.directLinkedGroupFetchFailed; return state.directLinkedGroupFetchFailed;
} }
/** export default storeModule({
* @typedef DirectLinkedStore
*
* // Actions
* @prop {typeof setDirectLinkedGroupFetchFailed} setDirectLinkedGroupFetchFailed
* @prop {typeof setDirectLinkedGroupId} setDirectLinkedGroupId
* @prop {typeof setDirectLinkedAnnotationId} setDirectLinkedAnnotationId
* @prop {typeof clearDirectLinkedGroupFetchFailed} clearDirectLinkedGroupFetchFailed
* @prop {typeof clearDirectLinkedIds} clearDirectLinkedIds
*
* // Selectors
* @prop {() => string|null} directLinkedAnnotationId
* @prop {() => boolean} directLinkedGroupFetchFailed
* @prop {() => string|null} directLinkedGroupId
*/
export default {
init, init,
namespace: 'directLinked', namespace: 'directLinked',
update, update,
...@@ -172,4 +158,4 @@ export default { ...@@ -172,4 +158,4 @@ export default {
directLinkedGroupFetchFailed, directLinkedGroupFetchFailed,
directLinkedGroupId, directLinkedGroupId,
}, },
}; });
...@@ -2,6 +2,7 @@ import { createSelector } from 'reselect'; ...@@ -2,6 +2,7 @@ import { createSelector } from 'reselect';
import * as metadata from '../../util/annotation-metadata'; import * as metadata from '../../util/annotation-metadata';
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
/** @typedef {import('../../../types/api').Annotation} Annotation */ /** @typedef {import('../../../types/api').Annotation} Annotation */
...@@ -98,7 +99,7 @@ function createDraft(annotation, changes) { ...@@ -98,7 +99,7 @@ function createDraft(annotation, changes) {
*/ */
function deleteNewAndEmptyDrafts() { function deleteNewAndEmptyDrafts() {
const { default: annotations } = require('./annotations'); const { removeAnnotations } = require('./annotations');
return (dispatch, getState) => { return (dispatch, getState) => {
const newDrafts = getState().drafts.filter(draft => { const newDrafts = getState().drafts.filter(draft => {
...@@ -111,7 +112,7 @@ function deleteNewAndEmptyDrafts() { ...@@ -111,7 +112,7 @@ function deleteNewAndEmptyDrafts() {
dispatch(removeDraft(draft.annotation)); dispatch(removeDraft(draft.annotation));
return draft.annotation; return draft.annotation;
}); });
dispatch(annotations.actions.removeAnnotations(removedAnnotations)); dispatch(removeAnnotations(removedAnnotations));
}; };
} }
...@@ -187,23 +188,7 @@ const unsavedAnnotations = createSelector( ...@@ -187,23 +188,7 @@ const unsavedAnnotations = createSelector(
drafts => drafts.filter(d => !d.annotation.id).map(d => d.annotation) drafts => drafts.filter(d => !d.annotation.id).map(d => d.annotation)
); );
/** export default storeModule({
* @typedef DraftsStore
*
* // Actions
* @prop {typeof createDraft} createDraft
* @prop {typeof deleteNewAndEmptyDrafts} deleteNewAndEmptyDrafts
* @prop {typeof discardAllDrafts} discardAllDrafts
* @prop {typeof removeDraft} removeDraft
*
* // Selectors
* @prop {() => number} countDrafts
* @prop {(a: Annotation) => Draft|null} getDraft
* @prop {(a: Annotation) => Draft|null} getDraftIfNotEmpty
* @prop {() => Annotation[]} unsavedAnnotations
*/
export default {
init, init,
namespace: 'drafts', namespace: 'drafts',
update, update,
...@@ -220,4 +205,4 @@ export default { ...@@ -220,4 +205,4 @@ export default {
getDraftIfNotEmpty, getDraftIfNotEmpty,
unsavedAnnotations, unsavedAnnotations,
}, },
}; });
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { actionTypes } from '../util'; import { actionTypes } from '../util';
import { storeModule } from '../create-store';
/** /**
* Manage state pertaining to the filtering of annotations in the UI. * Manage state pertaining to the filtering of annotations in the UI.
...@@ -270,25 +271,7 @@ function hasAppliedFilter(state) { ...@@ -270,25 +271,7 @@ function hasAppliedFilter(state) {
return !!(state.query || Object.keys(getFilters(state)).length); return !!(state.query || Object.keys(getFilters(state)).length);
} }
/** export default storeModule({
* @typedef FiltersStore
*
* // Actions
* @prop {typeof changeFocusModeUser} changeFocusModeUser
* @prop {typeof setFilter} setFilter
* @prop {typeof setFilterQuery} setFilterQuery
* @prop {typeof toggleFocusMode} toggleFocusMode
*
* // Selectors
* @prop {() => string|null} filterQuery
* @prop {() => FocusState} focusState
* @prop {(filterName: string) => FilterOption|undefined} getFilter
* @prop {() => Object<string,FilterOption>} getFilters
* @prop {() => Object<string,string>} getFilterValues
* @prop {() => boolean} hasAppliedFilter
*/
export default {
init, init,
namespace: 'filters', namespace: 'filters',
update, update,
...@@ -306,4 +289,4 @@ export default { ...@@ -306,4 +289,4 @@ export default {
getFilterValues, getFilterValues,
hasAppliedFilter, hasAppliedFilter,
}, },
}; });
...@@ -6,6 +6,7 @@ import { ...@@ -6,6 +6,7 @@ import {
import shallowEqual from 'shallowequal'; import shallowEqual from 'shallowequal';
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
/** /**
* @typedef {import('../../../types/annotator').DocumentMetadata} DocumentMetadata * @typedef {import('../../../types/annotator').DocumentMetadata} DocumentMetadata
...@@ -154,21 +155,7 @@ const searchUris = createShallowEqualSelector( ...@@ -154,21 +155,7 @@ const searchUris = createShallowEqualSelector(
uris => uris uris => uris
); );
/** export default storeModule({
* @typedef FramesStore
*
* // Actions
* @prop {typeof connectFrame} connectFrame
* @prop {typeof destroyFrame} destroyFrame
* @prop {typeof updateFrameAnnotationFetchStatus} updateFrameAnnotationFetchStatus
*
* // Selectors
* @prop {() => Frame[]} frames
* @prop {() => Frame|null} mainFrame
* @prop {() => string[]} searchUris
*/
export default {
init: init, init: init,
namespace: 'frames', namespace: 'frames',
update: update, update: update,
...@@ -184,4 +171,4 @@ export default { ...@@ -184,4 +171,4 @@ export default {
mainFrame, mainFrame,
searchUris, searchUris,
}, },
}; });
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
import session from './session'; import session from './session';
...@@ -196,28 +197,7 @@ const getCurrentlyViewingGroups = createSelector( ...@@ -196,28 +197,7 @@ const getCurrentlyViewingGroups = createSelector(
} }
); );
/** export default storeModule({
* @typedef GroupsStore
*
* // Actions
* @prop {typeof focusGroup} focusGroup
* @prop {typeof loadGroups} loadGroups
* @prop {typeof clearGroups} clearGroups
*
* // Selectors
* @prop {() => Group[]} allGroups
* @prop {() => Group|undefined|null} focusedGroup
* @prop {() => string|null} focusedGroupId
* @prop {() => Group[]} getFeaturedGroups
* @prop {(id: string) => Group|undefined} getGroup
* @prop {() => Group[]} getInScopeGroups
*
* // Root selectors
* @prop {() => Group[]} getCurrentlyViewingGroups,
* @prop {() => Group[]} getMyGroups,
*/
export default {
init, init,
namespace: 'groups', namespace: 'groups',
update, update,
...@@ -238,4 +218,4 @@ export default { ...@@ -238,4 +218,4 @@ export default {
getCurrentlyViewingGroups, getCurrentlyViewingGroups,
getMyGroups, getMyGroups,
}, },
}; });
import { storeModule } from '../create-store';
import { actionTypes } from '../util'; import { actionTypes } from '../util';
/** /**
...@@ -32,19 +34,12 @@ function updateLinks(newLinks) { ...@@ -32,19 +34,12 @@ function updateLinks(newLinks) {
}; };
} }
/** export default storeModule({
* @typedef LinksStore init,
*
* // Actions
* @prop {typeof updateLinks} updateLinks
*/
export default {
init: init,
namespace: 'links', namespace: 'links',
update, update,
actions: { actions: {
updateLinks, updateLinks,
}, },
selectors: {}, selectors: {},
}; });
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { storeModule } from '../create-store';
import { actionTypes } from '../util'; import { actionTypes } from '../util';
import annotations from './annotations'; import annotations from './annotations';
...@@ -182,21 +183,7 @@ function hasPendingDeletion(state, id) { ...@@ -182,21 +183,7 @@ function hasPendingDeletion(state, id) {
return state.pendingDeletions.hasOwnProperty(id); return state.pendingDeletions.hasOwnProperty(id);
} }
/** export default storeModule({
* @typedef RealTimeUpdatesStore
*
* // Actions
* @prop {typeof receiveRealTimeUpdates} receiveRealTimeUpdates
* @prop {typeof clearPendingUpdates} clearPendingUpdates
*
* // Selectors
* @prop {() => boolean} hasPendingDeletion
* @prop {() => Object.<string, boolean>} pendingDeletions
* @prop {() => Object.<string, Annotation>} pendingUpdates
* @prop {() => number} pendingUpdateCount
*/
export default {
init, init,
namespace: 'realTimeUpdates', namespace: 'realTimeUpdates',
update, update,
...@@ -210,4 +197,4 @@ export default { ...@@ -210,4 +197,4 @@ export default {
pendingUpdates, pendingUpdates,
pendingUpdateCount, pendingUpdateCount,
}, },
}; });
import { actionTypes } from '../util'; import { actionTypes } from '../util';
import { storeModule } from '../create-store';
function init() { function init() {
return { return {
/** /**
...@@ -56,18 +58,7 @@ function routeParams(state) { ...@@ -56,18 +58,7 @@ function routeParams(state) {
return state.params; return state.params;
} }
/** export default storeModule({
* @typedef RouteStore
*
* // Actions
* @prop {typeof changeRoute} changeRoute
*
* // Selectors
* @prop {() => string|null} route
* @prop {() => Object.<string,string>} routeParams
*/
export default {
init, init,
namespace: 'route', namespace: 'route',
update, update,
...@@ -78,4 +69,4 @@ export default { ...@@ -78,4 +69,4 @@ export default {
route, route,
routeParams, routeParams,
}, },
}; });
...@@ -22,6 +22,7 @@ import uiConstants from '../../ui-constants'; ...@@ -22,6 +22,7 @@ import uiConstants from '../../ui-constants';
import * as metadata from '../../util/annotation-metadata'; import * as metadata from '../../util/annotation-metadata';
import { countIf, trueKeys, toTrueMap } from '../../util/collections'; import { countIf, trueKeys, toTrueMap } from '../../util/collections';
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
/** /**
* Default sort keys for each tab. * Default sort keys for each tab.
...@@ -376,31 +377,7 @@ const sortKeys = createSelector( ...@@ -376,31 +377,7 @@ const sortKeys = createSelector(
} }
); );
/** export default storeModule({
* @typedef SelectionStore
*
* // Actions
* @prop {typeof clearSelection} clearSelection
* @prop {typeof selectAnnotations} selectAnnotations
* @prop {typeof selectTab} selectTab
* @prop {typeof setExpanded} setExpanded
* @prop {typeof setForcedVisible} setForcedVisible
* @prop {typeof setSortKey} setSortKey
* @prop {typeof toggleSelectedAnnotations} toggleSelectedAnnotations
*
* // Selectors
* @prop {() => Object<string,boolean>} expandedMap
* @prop {() => string[]} forcedVisibleAnnotations
* @prop {() => boolean} hasSelectedAnnotations
* @prop {() => string[]} selectedAnnotations
* @prop {() => string} selectedTab
* @prop {() => SelectionState} selectionState
* @prop {() => string} sortKey
* @prop {() => string[]} sortKeys
*
*/
export default {
init: init, init: init,
namespace: 'selection', namespace: 'selection',
update: update, update: update,
...@@ -425,4 +402,4 @@ export default { ...@@ -425,4 +402,4 @@ export default {
sortKey, sortKey,
sortKeys, sortKeys,
}, },
}; });
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
/** /**
* @typedef {import('../../../types/api').Profile} Profile * @typedef {import('../../../types/api').Profile} Profile
*/ */
...@@ -91,20 +93,7 @@ function profile(state) { ...@@ -91,20 +93,7 @@ function profile(state) {
return state.profile; return state.profile;
} }
/** export default storeModule({
* @typedef SessionStore
*
* // Actions
* @prop {typeof hasFetchedProfile} hasFetchedProfile
*
* // Selectors
* @prop {() => boolean} hasFetchedProfile
* @prop {(feature: string) => boolean} isFeatureEnabled
* @prop {() => boolean} isLoggedIn
* @prop {() => Profile} profile
*/
export default {
init, init,
namespace: 'session', namespace: 'session',
update, update,
...@@ -119,4 +108,4 @@ export default { ...@@ -119,4 +108,4 @@ export default {
isLoggedIn, isLoggedIn,
profile, profile,
}, },
}; });
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
function init() { function init() {
return { return {
/* /*
...@@ -112,19 +114,7 @@ function isSidebarPanelOpen(state, panelName) { ...@@ -112,19 +114,7 @@ function isSidebarPanelOpen(state, panelName) {
return state.activePanelName === panelName; return state.activePanelName === panelName;
} }
/** export default storeModule({
* @typedef SidebarPanelsStore
*
* // Actions
* @prop {typeof openSidebarPanel} openSidebarPanel
* @prop {typeof closeSidebarPanel} closeSidebarPanel
* @prop {typeof toggleSidebarPanel} toggleSidebarPanel
*
* // Selectors
* @prop {(name: string) => boolean} isSidebarPanelOpen
*/
export default {
namespace: 'sidebarPanels', namespace: 'sidebarPanels',
init: init, init: init,
update: update, update: update,
...@@ -138,4 +128,4 @@ export default { ...@@ -138,4 +128,4 @@ export default {
selectors: { selectors: {
isSidebarPanelOpen, isSidebarPanelOpen,
}, },
}; });
import { storeModule } from '../create-store';
import * as util from '../util'; import * as util from '../util';
/** /**
...@@ -110,21 +112,7 @@ function hasMessage(state, type, text) { ...@@ -110,21 +112,7 @@ function hasMessage(state, type, text) {
}); });
} }
/** export default storeModule({
* @typedef ToastMessagesStore
*
* // Actions
* @prop {typeof addMessage} addToastMessage
* @prop {typeof removeMessage} removeToastMessage
* @prop {typeof updateMessage} updateToastMessage
*
* // Selectors
* @prop {(id: string) => (ToastMessage|undefined)} getToastMessage
* @prop {() => ToastMessage[]} getToastMessages
* @prop {(type: string, text: string) => boolean} hasToastMessage
*/
export default {
init, init,
namespace: 'toastMessages', namespace: 'toastMessages',
update, update,
...@@ -138,4 +126,4 @@ export default { ...@@ -138,4 +126,4 @@ export default {
getToastMessages: getMessages, getToastMessages: getMessages,
hasToastMessage: hasMessage, hasToastMessage: hasMessage,
}, },
}; });
import * as util from '../util'; import * as util from '../util';
import { storeModule } from '../create-store';
/** /**
* This module defines actions and state related to the display mode of the * This module defines actions and state related to the display mode of the
* sidebar. * sidebar.
...@@ -53,18 +55,7 @@ function hasSidebarOpened(state) { ...@@ -53,18 +55,7 @@ function hasSidebarOpened(state) {
return state.sidebarHasOpened; return state.sidebarHasOpened;
} }
/** export default storeModule({
* @typedef ViewerStore
*
* // Actions
* @prop {typeof setShowHighlights} setShowHighlights
* @prop {typeof setSidebarOpened} setSidebarOpened
*
* // Selectors
* @prop {() => boolean} hasSidebarOpened
*/
export default {
init: init, init: init,
namespace: 'viewer', namespace: 'viewer',
update: update, update: update,
...@@ -75,4 +66,4 @@ export default { ...@@ -75,4 +66,4 @@ export default {
selectors: { selectors: {
hasSidebarOpened, hasSidebarOpened,
}, },
}; });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment