2018-07-26 14:37:10 +01:00
|
|
|
import { merge } from 'rxjs/observable/merge';
|
|
|
|
import { empty } from 'rxjs/observable/empty';
|
|
|
|
import {
|
|
|
|
tap,
|
|
|
|
filter,
|
|
|
|
map,
|
|
|
|
ignoreElements,
|
|
|
|
switchMap,
|
|
|
|
catchError
|
|
|
|
} from 'rxjs/operators';
|
|
|
|
import { ofType } from 'redux-observable';
|
|
|
|
import store from 'store';
|
|
|
|
import uuid from 'uuid/v4';
|
|
|
|
|
|
|
|
import { types, onlineStatusChange, isOnlineSelector } from './';
|
|
|
|
import postUpdate$ from '../../templates/Challenges/utils/postUpdate$';
|
2018-07-27 12:58:12 +01:00
|
|
|
import { isGoodXHRStatus } from '../../templates/Challenges/utils';
|
2018-07-26 14:37:10 +01:00
|
|
|
|
|
|
|
const key = 'fcc-failed-updates';
|
|
|
|
|
|
|
|
function delay(time = 0, fn) {
|
|
|
|
return setTimeout(fn, time);
|
|
|
|
}
|
|
|
|
|
|
|
|
function failedUpdateEpic(action$, { getState }) {
|
|
|
|
const storeUpdates = action$.pipe(
|
|
|
|
ofType(types.updateFailed),
|
2018-07-27 12:58:12 +01:00
|
|
|
tap(({ payload = {} }) => {
|
|
|
|
if ('endpoint' in payload && 'payload' in payload) {
|
|
|
|
const failures = store.get(key) || [];
|
|
|
|
payload.id = uuid();
|
|
|
|
store.set(key, [...failures, payload]);
|
|
|
|
}
|
2018-07-26 14:37:10 +01:00
|
|
|
}),
|
|
|
|
map(() => onlineStatusChange(false))
|
|
|
|
);
|
|
|
|
|
|
|
|
const flushUpdates = action$.pipe(
|
|
|
|
ofType(types.fetchUserComplete, types.updateComplete),
|
|
|
|
filter(() => store.get(key)),
|
|
|
|
filter(() => isOnlineSelector(getState())),
|
|
|
|
tap(() => {
|
|
|
|
const failures = store.get(key) || [];
|
2018-07-27 20:52:00 +01:00
|
|
|
let delayTime = 100;
|
|
|
|
const batch = failures.map((update, i) => {
|
2018-07-26 14:37:10 +01:00
|
|
|
// we stagger the updates here so we don't hammer the server
|
2018-07-27 20:52:00 +01:00
|
|
|
// *********************************************************
|
|
|
|
// progressivly increase additional delay by the amount of updates
|
|
|
|
// 1st: 100ms delay
|
|
|
|
// 2nd: 200ms delay
|
|
|
|
// 3rd: 400ms delay
|
|
|
|
// 4th: 700ms delay
|
|
|
|
// 5th: 1100ms delay
|
|
|
|
// 6th: 1600ms delay
|
|
|
|
// and so-on
|
|
|
|
delayTime += 100 * i;
|
2018-07-26 14:37:10 +01:00
|
|
|
return delay(delayTime, () =>
|
|
|
|
postUpdate$(update)
|
|
|
|
.pipe(
|
|
|
|
switchMap(response => {
|
2018-07-27 12:58:12 +01:00
|
|
|
if (
|
|
|
|
response &&
|
|
|
|
(response.message || isGoodXHRStatus(response.status))
|
|
|
|
) {
|
|
|
|
console.info(`${update.id} succeeded`);
|
2018-07-26 14:37:10 +01:00
|
|
|
// the request completed successfully
|
|
|
|
const failures = store.get(key) || [];
|
|
|
|
const newFailures = failures.filter(x => x.id !== update.id);
|
|
|
|
store.set(key, newFailures);
|
|
|
|
}
|
|
|
|
return empty();
|
|
|
|
}),
|
|
|
|
catchError(() => empty())
|
|
|
|
)
|
|
|
|
.toPromise()
|
|
|
|
);
|
|
|
|
});
|
|
|
|
Promise.all(batch)
|
|
|
|
.then(() => console.info('progress updates processed where possible'))
|
|
|
|
.catch(err =>
|
|
|
|
console.warn('unable to process progress updates', err.message)
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
ignoreElements()
|
|
|
|
);
|
|
|
|
|
|
|
|
return merge(storeUpdates, flushUpdates);
|
|
|
|
}
|
|
|
|
|
|
|
|
export default failedUpdateEpic;
|