react-native-offline
Version:
Handy toolbelt to deal with offline mode in React Native applications. Cross-platform, provides a smooth redux integration.
135 lines (121 loc) • 3.73 kB
text/typescript
import get from 'lodash/get';
import without from 'lodash/without';
import { AnyAction } from 'redux';
import * as actionTypes from './actionTypes';
import { SEMAPHORE_COLOR } from '../utils/constants';
import getSimilarActionInQueue from '../utils/getSimilarActionInQueue';
import {
NetworkState,
EnqueuedAction,
FluxAction,
Thunk,
SemaphoreColor,
} from '../types';
import { ReduxActions, FetchOfflineModeType } from './actionCreators';
import nonNullable from '../utils/nonNullable';
type ComparisonFn = (
action: any,
actionQueue: EnqueuedAction[],
) => FluxAction<any> | Thunk | undefined;
const actionQueue: EnqueuedAction[] = [];
export const initialState = {
isConnected: true,
actionQueue,
isQueuePaused: false,
};
function handleOfflineAction(
state: NetworkState,
{ payload: { prevAction, prevThunk }, meta }: FetchOfflineModeType,
comparisonFn: ComparisonFn,
): NetworkState {
const isActionToRetry =
typeof prevAction === 'object' && get(meta, 'retry') === true;
const isThunkToRetry =
typeof prevThunk === 'function' && get(prevThunk, 'meta.retry') === true;
if (isActionToRetry || isThunkToRetry) {
// If a similar action already existed on the queue, we remove it and push it again to the end of the queue
const actionToLookUp = prevAction || prevThunk;
const actionWithMetaData =
typeof actionToLookUp === 'object'
? ({ ...actionToLookUp, meta } as FluxAction)
: actionToLookUp;
const similarActionQueued = comparisonFn(
actionWithMetaData,
state.actionQueue,
);
const newActionQueue = similarActionQueued
? [...without(state.actionQueue, similarActionQueued), actionWithMetaData]
: [...state.actionQueue, actionWithMetaData];
return {
...state,
actionQueue: newActionQueue.filter(nonNullable),
};
}
return state;
}
function handleRemoveActionFromQueue(
state: NetworkState,
action: EnqueuedAction,
): NetworkState {
const similarActionQueued = getSimilarActionInQueue(
action,
state.actionQueue,
);
return {
...state,
actionQueue: without(state.actionQueue, similarActionQueued).filter(
nonNullable,
),
};
}
function handleDismissActionsFromQueue(
state: NetworkState,
triggerActionToDismiss: string,
): NetworkState {
const newActionQueue = state.actionQueue.filter(action => {
const dismissArray = get(action, 'meta.dismiss', []);
return !dismissArray.includes(triggerActionToDismiss);
});
return {
...state,
actionQueue: newActionQueue,
};
}
function handleChangeQueueSemaphore(
state: NetworkState,
semaphoreColor: SemaphoreColor,
): NetworkState {
return {
...state,
isQueuePaused: semaphoreColor === SEMAPHORE_COLOR.RED,
};
}
export default (comparisonFn: ComparisonFn = getSimilarActionInQueue) => (
state: NetworkState = initialState,
action: ReduxActions | AnyAction,
): NetworkState => {
switch (action.type) {
case actionTypes.CONNECTION_CHANGE:
return {
...state,
isConnected: action.payload,
};
case actionTypes.FETCH_OFFLINE_MODE:
return handleOfflineAction(
state,
action as FetchOfflineModeType,
comparisonFn,
);
case actionTypes.REMOVE_FROM_ACTION_QUEUE:
return handleRemoveActionFromQueue(state, action.payload);
case actionTypes.DISMISS_ACTIONS_FROM_QUEUE:
return handleDismissActionsFromQueue(state, action.payload);
case actionTypes.CHANGE_QUEUE_SEMAPHORE:
return handleChangeQueueSemaphore(state, action.payload);
default:
return state;
}
};
export function networkSelector(state: { network: NetworkState }) {
return state.network;
}