{"size": 2799, "ext": "js", "max_stars_repo_path": "src/middleware.js", "max_stars_repo_name": "RianNote/redux-offline", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "src/middleware.js", "max_issues_repo_name": "RianNote/redux-offline", "max_issues_repo_issues_event_min_datetime": "2021-05-09T03:20:45.000Z", "max_issues_repo_issues_event_max_datetime": "2022-03-15T20:32:25.000Z", "max_forks_repo_path": "src/middleware.js", "max_forks_repo_name": "roth1002/redux-offline", "max_forks_repo_forks_event_min_datetime": "2017-06-29T18:02:44.000Z", "max_forks_repo_forks_event_max_datetime": "2017-06-29T18:02:44.000Z"}
{"max_stars_count": null, "max_issues_count": 4, "max_forks_count": 1, "avg_line_length": 32.9294117647, "max_line_length": 94, "alphanum_fraction": 0.6559485531}
// @flow

import type { AppState, Config, OfflineAction, ResultAction, Outbox } from './types';
import { OFFLINE_SEND, OFFLINE_SCHEDULE_RETRY } from './constants';
import { completeRetry, scheduleRetry, busy } from './actions';

const after = (timeout = 0) => {
  return new Promise(resolve => setTimeout(resolve, timeout));
};

const complete = (action: ResultAction, success: boolean, payload: {}): ResultAction => {
  return { ...action, payload, meta: { ...action.meta, success, completed: true } };
};

const take = (state: AppState, config: Config): Outbox => {
  // batching is optional, for now
  if (config.batch) {
    return config.batch(state.offline.outbox);
  }

  return [state.offline.outbox[0]];
};

const send = (action: OfflineAction, dispatch, config: Config, retries = 0) => {
  const metadata = action.meta.offline;
  dispatch(busy(true));
  return config
    .effect(metadata.effect, action)
    .then(result => dispatch(complete(metadata.commit, true, result)))
    .catch(error => {
      // discard
      if (config.discard(error, action, retries)) {
        console.log('Discarding action', action.type);
        return dispatch(complete(metadata.rollback, false, error));
      }
      const delay = config.retry(action, retries);
      if (delay != null) {
        console.log('Retrying action', action.type, 'with delay', delay);
        return dispatch(scheduleRetry(delay));
      } else {
        console.log('Discarding action', action.type, 'because retry did not return a delay');
        return dispatch(complete(metadata.rollback, false, error));
      }
    });
};

export const createOfflineMiddleware = (config: Config) => (store: any) => (next: any) => (
  action: any
) => {
  // allow other middleware to do their things
  const result = next(action);

  // find any actions to send, if any
  const state: AppState = store.getState();
  const actions = take(state, config);

  // if the are any actions in the queue that we are not
  // yet processing, send those actions
  if (
    actions.length > 0 &&
    !state.offline.busy &&
    !state.offline.retryScheduled &&
    state.offline.online
  ) {
    send(actions[0], store.dispatch, config, state.offline.retryCount);
  }

  if (action.type === OFFLINE_SCHEDULE_RETRY) {
    const retryToken = state.offline.retryToken;
    after(action.payload.delay).then(() => store.dispatch(completeRetry(retryToken)));
  }

  // if (action.type === 'Offline/COMPLETE_RETRY') {
  //   if (action.meta.retryToken === state.offline.retryToken && actions.length > 0) {
  //     send(actions[0], store.dispatch, config);
  //   }
  // }

  if (action.type === OFFLINE_SEND && actions.length > 0 && !state.offline.busy) {
    send(actions[0], store.dispatch, config, state.offline.retryCount);
  }

  return result;
};
