UNPKG

topcoder-react-lib

Version:
148 lines (131 loc) 4.35 kB
/** * @module "reduces.member-tasks" * @desc Member tasks reducer. * @todo Document state segment structure. */ /* global alert */ import _ from 'lodash'; import { isomorphy, redux } from 'topcoder-react-utils'; import logger from '../utils/logger'; import actions, { PAGE_SIZE } from '../actions/member-tasks'; /** * Drops all tasks and cancels the ongoing loading operation, if it is pending. * @param {Object} state * @return {Object} New state. */ function onDropAll(state) { return { ...state, allLoaded: false, lastRequestedPageNum: -1, loadingUuid: '', tasks: [], timestamp: 0, }; } /** * Stores into the state meta data about the initiated loading operation. * This will effectively cancel the already pending loading operation, if any. * @param {Object} state * @param {Object} action * @return {Object} New state. */ function onGetInit(state, { payload }) { return { ...state, lastRequestedPageNum: payload.pageNum, loadingUuid: payload.uuid, }; } /** * Handles the actual result of the loading operation. * @param {Object} state * @param {Object} action * @return {Object} New state. */ function onGetDone(state, { error, payload }) { /* Bails out in case of error. */ if (error) { logger.error(payload); /* NOTE: For now, using alert to inform about failures is kind of fine. */ /* eslint-disable no-alert */ if (isomorphy.isClientSide()) alert('Failed to load member tasks'); /* eslint-enable no-alert */ return state; } /* Silently ignores the action, if it has unexpected UUID. */ const { projectId, tasks, uuid } = payload; if (uuid !== state.loadingUuid) return state; /* Generates the map of old tasks, and the count of old tasks related to * the specified project. */ const taskMap = {}; state.tasks.forEach((task) => { taskMap[task.id] = task; }); /* Merges newly loaded tasks into the map of old ones. */ tasks.forEach((task) => { taskMap[task.id] = task; }); /* If the first page of tasks has been loaded, updates its timestamp. */ let { timestamps } = state; if (!state.lastRequestedPageNum) { timestamps = _.clone(timestamps); timestamps[projectId] = Date.now(); } return { ...state, allLoaded: tasks.length < PAGE_SIZE, loadingUuid: '', tasks: _.values(taskMap), timestamps, }; } /** * Creates a new Member tasks reducer with the specified initial state. * @param {Object} initialState Optional. Initial state. * @return {Function} Member tasks reducer. */ function create(initialState = {}) { const a = actions.memberTasks; return redux.handleActions({ [a.dropAll]: onDropAll, [a.getInit]: onGetInit, [a.getDone]: onGetDone, }, _.defaults(initialState, { /* It is set true when a request to load tasks loads less tasks than a full * task page size. It is reset to false each time the page number 0 starts * to load. If you load task pages sequentially from the very first page, * with the same projectId and user specified by auth token, this flag * signals you when there is no more tasks to load. */ allLoaded: false, /* Holds the number of the last requested task page. */ lastRequestedPageNum: -1, /* Equals to the loading operation UUID when the result is pending; equals * empty string when nothing is being loaded. */ loadingUuid: '', /* The list of tasks loaded so far. For better performance and user * experience it may contain tasks related to projects queried before; * be sure to filter it according to your needs. */ tasks: [], /* Keys of this object are projectIds, and values keep the timestamps * corresponding to latest updates of the first page of tasks related * to those projects. */ timestamps: {}, })); } /** * Factory which creates a new reducer with its initial state tailored to the * given options object, if specified (for server-side rendering). If options * object is not specified, it creates just the default reducer. Accepted options are: * @return {Promise} * @resolves {Function(state, action): state} New reducer. */ export function factory() { return Promise.resolve(create()); } /** * @static * @member default * @desc Reducer with default initial state. */ export default create();