|
|
@@ -1,137 +1,177 @@
|
|
|
-import { assignTo } from '../util/object'
|
|
|
+import { EventSource, EventSourceHash, getEventSourceDef } from '../structs/event-source'
|
|
|
import Calendar from '../Calendar'
|
|
|
+import { arrayToHash, assignTo, filterHash } from '../util/object'
|
|
|
+import { DateRange } from '../datelib/date-range'
|
|
|
import { warn } from '../util/misc'
|
|
|
-import { EventSource, EventSourceHash, parseEventSource, getEventSourceDef } from '../structs/event-source'
|
|
|
+import { DateProfile } from '../DateProfileGenerator'
|
|
|
+import { Action, SimpleError } from './types'
|
|
|
|
|
|
-let uid = 0
|
|
|
+export default function(eventSourceHash: EventSourceHash, action: Action, dateProfile: DateProfile | null, calendar: Calendar): EventSourceHash {
|
|
|
+ switch (action.type) {
|
|
|
|
|
|
-// reducers
|
|
|
+ case 'ADD_EVENT_SOURCES': // already parsed
|
|
|
+ return addSources(eventSourceHash, action.sources, dateProfile, calendar)
|
|
|
|
|
|
-export function reduceEventSourceHash(sourceHash: EventSourceHash, action: any, calendar: Calendar): EventSourceHash {
|
|
|
- let eventSource: EventSource
|
|
|
+ case 'REMOVE_EVENT_SOURCES':
|
|
|
+ if (action.sourceIds) {
|
|
|
+ return removeSources(eventSourceHash, action.sourceIds)
|
|
|
+ } else {
|
|
|
+ return {} // remove all
|
|
|
+ }
|
|
|
|
|
|
- switch (action.type) {
|
|
|
+ case 'SET_DATE_PROFILE':
|
|
|
+ fetchDirtySources(eventSourceHash, action.dateProfile, calendar)
|
|
|
+ return eventSourceHash
|
|
|
|
|
|
- case 'ADD_EVENT_SOURCE':
|
|
|
- eventSource = parseEventSource(action.rawSource)
|
|
|
-
|
|
|
- if (eventSource) {
|
|
|
- if (calendar.state.dateProfile) {
|
|
|
- calendar.dispatch({
|
|
|
- type: 'FETCH_EVENT_SOURCE',
|
|
|
- sourceId: eventSource.sourceId,
|
|
|
- range: calendar.state.dateProfile.activeRange
|
|
|
- })
|
|
|
- }
|
|
|
- return assignTo({}, sourceHash, {
|
|
|
- [eventSource.sourceId]: eventSource
|
|
|
- })
|
|
|
+ case 'FETCH_EVENT_SOURCES':
|
|
|
+ if (dateProfile) {
|
|
|
+ return fetchSourcesById(eventSourceHash, action.sourceIds || null, dateProfile, calendar)
|
|
|
} else {
|
|
|
- return sourceHash
|
|
|
+ return eventSourceHash // can't fetch if don't know the framing range
|
|
|
}
|
|
|
|
|
|
- case 'FETCH_ALL_EVENT_SOURCES':
|
|
|
- for (let sourceId in sourceHash) {
|
|
|
- calendar.dispatch({
|
|
|
- type: 'FETCH_EVENT_SOURCE',
|
|
|
- sourceId,
|
|
|
- range: calendar.state.dateProfile.activeRange
|
|
|
- })
|
|
|
- }
|
|
|
- return sourceHash
|
|
|
+ case 'RECEIVE_EVENTS':
|
|
|
+ case 'RECEIVE_EVENT_ERROR':
|
|
|
+ return receiveResponse(eventSourceHash, action.sourceId, action.fetchId, action.fetchRange)
|
|
|
+
|
|
|
+ default:
|
|
|
+ return eventSourceHash
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- case 'FETCH_EVENT_SOURCE':
|
|
|
- eventSource = sourceHash[action.sourceId]
|
|
|
+let uid = 0
|
|
|
|
|
|
+function addSources(eventSourceHash: EventSourceHash, sources: EventSource[], dateProfile: DateProfile | null, calendar: Calendar): EventSourceHash {
|
|
|
+ let hash: EventSourceHash = {}
|
|
|
+
|
|
|
+ for (let source of sources) {
|
|
|
+ hash[source.sourceId] = source
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dateProfile) {
|
|
|
+ fetchDirtySources(hash, dateProfile, calendar)
|
|
|
+ }
|
|
|
+
|
|
|
+ return assignTo({}, eventSourceHash, hash)
|
|
|
+}
|
|
|
+
|
|
|
+function removeSources(eventSourceHash: EventSourceHash, sourceIds: string[]): EventSourceHash {
|
|
|
+ let idHash = arrayToHash(sourceIds)
|
|
|
+
|
|
|
+ return filterHash(eventSourceHash, function(eventSource: EventSource) {
|
|
|
+ return !idHash[eventSource.sourceId]
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function fetchDirtySources(sourceHash: EventSourceHash, dateProfile: DateProfile, calendar: Calendar) {
|
|
|
+ let activeRange = dateProfile.activeRange
|
|
|
+ let dirtySourceIds = []
|
|
|
+
|
|
|
+ for (let sourceId in sourceHash) {
|
|
|
+ let eventSource = sourceHash[sourceId]
|
|
|
+
|
|
|
+ if (
|
|
|
+ !calendar.opt('lazyFetching') ||
|
|
|
+ !eventSource.fetchRange ||
|
|
|
+ eventSource.fetchRange.start < activeRange.start ||
|
|
|
+ eventSource.fetchRange.end > activeRange.end
|
|
|
+ ) {
|
|
|
+ dirtySourceIds.push(eventSource.sourceId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dirtySourceIds.length) {
|
|
|
+ calendar.dispatch({
|
|
|
+ type: 'FETCH_EVENT_SOURCES',
|
|
|
+ sourceIds: dirtySourceIds
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function fetchSourcesById(
|
|
|
+ prevSources: EventSourceHash,
|
|
|
+ sourceIds: string[] | null,
|
|
|
+ dateProfile: DateProfile,
|
|
|
+ calendar: Calendar
|
|
|
+): EventSourceHash {
|
|
|
+ let sourceIdHash = sourceIds ? arrayToHash(sourceIds) : null
|
|
|
+ let nextSources: EventSourceHash = {}
|
|
|
+ let activeRange = dateProfile.activeRange
|
|
|
+
|
|
|
+ for (let sourceId in prevSources) {
|
|
|
+ let source = prevSources[sourceId]
|
|
|
+
|
|
|
+ if (!sourceIdHash || sourceIdHash[sourceId]) {
|
|
|
let fetchId = String(uid++)
|
|
|
|
|
|
- getEventSourceDef(eventSource.sourceDefId).fetch(
|
|
|
- {
|
|
|
- eventSource,
|
|
|
- calendar,
|
|
|
- range: action.range
|
|
|
- },
|
|
|
- function(rawEvents) {
|
|
|
- calendar.dispatch({
|
|
|
- type: 'RECEIVE_EVENT_SOURCE',
|
|
|
- sourceId: eventSource.sourceId,
|
|
|
- fetchId,
|
|
|
- fetchRange: action.range,
|
|
|
- rawEvents
|
|
|
- })
|
|
|
- },
|
|
|
- function(errorInput) {
|
|
|
- let errorObj
|
|
|
-
|
|
|
- if (typeof errorInput === 'string') {
|
|
|
- errorObj = { message: errorInput }
|
|
|
- } else {
|
|
|
- errorObj = errorInput || {}
|
|
|
- }
|
|
|
-
|
|
|
- calendar.dispatch({
|
|
|
- type: 'ERROR_EVENT_SOURCE',
|
|
|
- sourceId: eventSource.sourceId,
|
|
|
- fetchId,
|
|
|
- fetchRange: action.range,
|
|
|
- error: errorObj
|
|
|
- })
|
|
|
- }
|
|
|
- )
|
|
|
-
|
|
|
- return assignTo({}, sourceHash, {
|
|
|
- [eventSource.sourceId]: assignTo({}, eventSource, {
|
|
|
- isFetching: true,
|
|
|
- latestFetchId: fetchId
|
|
|
- })
|
|
|
+ fetchSource(source, activeRange, fetchId, calendar)
|
|
|
+
|
|
|
+ nextSources[sourceId] = assignTo({}, source, {
|
|
|
+ isFetching: true,
|
|
|
+ latestFetchId: fetchId
|
|
|
})
|
|
|
+ } else {
|
|
|
+ nextSources[sourceId] = source
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- case 'RECEIVE_EVENT_SOURCE':
|
|
|
- case 'ERROR_EVENT_SOURCE':
|
|
|
- eventSource = sourceHash[action.sourceId]
|
|
|
-
|
|
|
- if (eventSource.latestFetchId === action.fetchId) {
|
|
|
-
|
|
|
- if (action.type === 'RECEIVE_EVENT_SOURCE') {
|
|
|
- eventSource.success(action.rawEvents)
|
|
|
- } else { // failure
|
|
|
- warn(action.error.message, action.error)
|
|
|
- eventSource.failure(action.error)
|
|
|
- }
|
|
|
-
|
|
|
- return assignTo({}, sourceHash, {
|
|
|
- [eventSource.sourceId]: assignTo({}, eventSource, {
|
|
|
- isFetching: false,
|
|
|
- fetchRange: action.fetchRange
|
|
|
- })
|
|
|
- })
|
|
|
- } else {
|
|
|
- return sourceHash
|
|
|
- }
|
|
|
+ return nextSources
|
|
|
+}
|
|
|
|
|
|
- case 'SET_DATE_PROFILE':
|
|
|
- let activeRange = action.dateProfile.activeRange
|
|
|
-
|
|
|
- for (let sourceId in sourceHash) {
|
|
|
- eventSource = sourceHash[sourceId]
|
|
|
-
|
|
|
- if (
|
|
|
- !calendar.opt('lazyFetching') ||
|
|
|
- !eventSource.fetchRange ||
|
|
|
- eventSource.fetchRange.start < activeRange.start ||
|
|
|
- eventSource.fetchRange.end > activeRange.end
|
|
|
- ) {
|
|
|
- calendar.dispatch({
|
|
|
- type: 'FETCH_EVENT_SOURCE',
|
|
|
- sourceId: eventSource.sourceId,
|
|
|
- range: activeRange
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
+function fetchSource(eventSource: EventSource, range: DateRange, fetchId: string, calendar: Calendar) {
|
|
|
+ getEventSourceDef(eventSource.sourceDefId).fetch(
|
|
|
+ {
|
|
|
+ eventSource,
|
|
|
+ calendar,
|
|
|
+ range
|
|
|
+ },
|
|
|
+ function(rawEvents) {
|
|
|
+ eventSource.success(rawEvents)
|
|
|
+
|
|
|
+ calendar.dispatch({
|
|
|
+ type: 'RECEIVE_EVENTS',
|
|
|
+ sourceId: eventSource.sourceId,
|
|
|
+ fetchId,
|
|
|
+ fetchRange: range,
|
|
|
+ rawEvents
|
|
|
+ })
|
|
|
+ },
|
|
|
+ function(errorInput) {
|
|
|
+ let error = normalizeError(errorInput)
|
|
|
+
|
|
|
+ warn(error.message, error)
|
|
|
+ eventSource.failure(error)
|
|
|
+
|
|
|
+ calendar.dispatch({
|
|
|
+ type: 'RECEIVE_EVENT_ERROR',
|
|
|
+ sourceId: eventSource.sourceId,
|
|
|
+ fetchId,
|
|
|
+ fetchRange: range,
|
|
|
+ error
|
|
|
+ })
|
|
|
+ }
|
|
|
+ )
|
|
|
+}
|
|
|
|
|
|
- return sourceHash
|
|
|
+function receiveResponse(sourceHash: EventSourceHash, sourceId: string, fetchId: string, fetchRange: DateRange) {
|
|
|
+ let eventSource: EventSource = sourceHash[sourceId]
|
|
|
|
|
|
- default:
|
|
|
- return sourceHash
|
|
|
+ if (fetchId === eventSource.latestFetchId) {
|
|
|
+ return assignTo({}, sourceHash, {
|
|
|
+ [sourceId]: assignTo({}, eventSource, {
|
|
|
+ isFetching: false,
|
|
|
+ fetchRange
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ return sourceHash
|
|
|
+}
|
|
|
+
|
|
|
+function normalizeError(input: any): SimpleError {
|
|
|
+ if (typeof input === 'string') {
|
|
|
+ return { message: input }
|
|
|
+ } else {
|
|
|
+ return input || {}
|
|
|
}
|
|
|
}
|