UNPKG

@twreporter/redux

Version:

redux actions and reducers for twreporter website

392 lines (379 loc) 17.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchAFullPost = fetchAFullPost; exports.fetchLatestPosts = fetchLatestPosts; exports.fetchPostsByCategoryListId = fetchPostsByCategoryListId; exports.fetchPostsByCategorySetListId = fetchPostsByCategorySetListId; exports.fetchPostsByTagListId = fetchPostsByTagListId; exports.fetchRelatedPostsOfAnEntity = fetchRelatedPostsOfAnEntity; var _url = require("../utils/url"); var _errorActionCreators = _interopRequireDefault(require("./error-action-creators")); var _apiConfig = _interopRequireDefault(require("../constants/api-config")); var _apiEndpoints = _interopRequireDefault(require("../constants/api-endpoints")); var _axios = _interopRequireDefault(require("axios")); var _reduxStateFieldNames = _interopRequireDefault(require("../constants/redux-state-field-names")); var _actionTypes = _interopRequireDefault(require("../constants/action-types")); var _latest = require("../constants/latest"); var _filter = _interopRequireDefault(require("lodash/filter")); var _get = _interopRequireDefault(require("lodash/get")); var _merge = _interopRequireDefault(require("lodash/merge")); var _split = _interopRequireDefault(require("lodash/split")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } // lodash var _ = { filter: _filter["default"], get: _get["default"], merge: _merge["default"], split: _split["default"] }; var entities = _reduxStateFieldNames["default"].entities, postsInEntities = _reduxStateFieldNames["default"].postsInEntities; /** * Fetch a full post, whose assets like relateds, leading_video ...etc are all complete, * @param {string} slug - slug of post * @param {string} jwt - access_token granted for the user * @param {number} [timeout=apiConfig.timeout] - request api timeout * @return {import('../typedef').Thunk} async action creator */ function fetchAFullPost(slug) { var jwt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; var timeout = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _apiConfig["default"].timeout; return function (dispatch, getState) { var state = getState(); var postId = _.get(state, [entities, postsInEntities, 'slugToId', slug], ''); var post = _.get(state, [entities, postsInEntities, 'byId', postId], null); // post is already fully fetched if (_.get(post, 'full', false)) { // current selected post is not the post just been fetched, // change the selected post if (slug !== _.get(state, "".concat(_reduxStateFieldNames["default"].selectedPost, ".slug"))) { var successAction = { type: _actionTypes["default"].selectedPost.read.alreadyExists, payload: { post: post } }; dispatch(successAction); return Promise.resolve(successAction); } // current selected post is the post just been fetched, // do nothing var action = { type: _actionTypes["default"].dataAlreadyExists, payload: { "function": fetchAFullPost.name, arguments: { slug: slug }, message: 'Post already exists in redux state.' } }; dispatch(action); return Promise.resolve(action); } var apiOrigin = _.get(state, [_reduxStateFieldNames["default"].origins, 'api']); var path = "/v2/".concat(_apiEndpoints["default"].posts, "/").concat(slug); var url = (0, _url.formURL)(apiOrigin, path, { full: 'true', toggleBookmark: 'true' }); // Start to get topics dispatch({ type: _actionTypes["default"].selectedPost.read.request, payload: { slug: slug } }); var config = { timeout: timeout }; if (jwt) { config.headers = { Authorization: "Bearer ".concat(jwt) }; config.withCredentials = true; } return _axios["default"].get(url, config).then(function (response) { var successAction = { type: _actionTypes["default"].selectedPost.read.success, payload: { post: _.get(response, 'data.data', {}) } }; dispatch(successAction); return successAction; })["catch"](function (error) { var failAction = _errorActionCreators["default"].axios(error, _actionTypes["default"].selectedPost.read.failure); failAction.payload['slug'] = slug; dispatch(failAction); return Promise.reject(failAction); }); }; } /** * @param {Function} dispatch - dispatch of redux * @param {string} url - URL to request * @param {string} successActionType - action type * @param {string} failureActionType - action type * @param {Object} defaultPayload * @param {number} timeout - request api timeout * @return {Promise} resolve with success action or reject with fail action **/ function _fetchPosts(dispatch, url, successActionType, failureActionType) { var defaultPayload = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var timeout = arguments.length > 5 ? arguments[5] : undefined; var jwt = arguments.length > 6 ? arguments[6] : undefined; var config = { timeout: timeout }; if (jwt) { config.headers = { Authorization: "Bearer ".concat(jwt) }; config.withCredentials = true; } return _axios["default"].get(url, config).then(function (response) { var successAction = { type: successActionType, payload: _.merge({ items: _.get(response, 'data.data.records', []), total: _.get(response, 'data.data.meta.total', 0) }, defaultPayload) }; dispatch(successAction); return successAction; })["catch"](function (error) { var failAction = _errorActionCreators["default"].axios(error, failureActionType); failAction.payload = _.merge(failAction.payload, defaultPayload); dispatch(failAction); return Promise.reject(failAction); }); } /** * Given ObjectID of a target entity (post or topic), this functions will load the related posts * of that target entity. * @param {import('../typedef').ObjectID} entityId - ObjectID of a entity, which could be a post or topic * @param {number} limit - specify how many posts to load * @param {number} [timeout=apiConfig.timeout] - request api timeout * @return {import('../typedef').Thunk} async action creator */ function fetchRelatedPostsOfAnEntity(entityId) { var limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 6; var timeout = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _apiConfig["default"].timeout; return function (dispatch, getState) { /** @type {import('../typedef').ReduxState} */ var state = getState(); var allPostIds = _.get(state, [_reduxStateFieldNames["default"].entities, _reduxStateFieldNames["default"].postsInEntities, 'allIds'], []); /** @type {import('../typedef').RelatedPostsOfAnEntity} */ var relatedsOfAnEntity = _.get(state, [_reduxStateFieldNames["default"].relatedPostsOf, 'byId', entityId], {}); var more = _.get(relatedsOfAnEntity, 'more', []); if (_.get(more, 'length', 0) === 0 || typeof limit !== 'number' || limit <= 0) { // no more posts to load var action = { type: _actionTypes["default"].relatedPosts.read.noMore, payload: { targetEntityId: entityId, limit: limit } }; dispatch(action); return Promise.resolve(action); } var targetRelatedPostsIds = more.slice(0, limit); // filter out those related posts already fetched var idsToRequest = _.filter(targetRelatedPostsIds, function (id) { return allPostIds.indexOf(id) === -1; }); // dispatch success action if related posts already fetched if (_.get(idsToRequest, 'length', 0) === 0) { var _action = { type: _actionTypes["default"].relatedPosts.read.success, payload: { targetEntityId: entityId, targetRelatedPostsIds: targetRelatedPostsIds } }; dispatch(_action); return Promise.resolve(_action); } var apiOrigin = _.get(state, [_reduxStateFieldNames["default"].origins, 'api']); var path = "/v2/".concat(_apiEndpoints["default"].posts); var url = (0, _url.formURL)(apiOrigin, path, { id: idsToRequest }); dispatch({ type: _actionTypes["default"].relatedPosts.read.request, payload: { url: url, targetEntityId: entityId } }); return _fetchPosts(dispatch, url, _actionTypes["default"].relatedPosts.read.success, _actionTypes["default"].relatedPosts.read.failure, { targetEntityId: entityId, targetRelatedPostsIds: targetRelatedPostsIds }, timeout); }; } var startPage = 1; /** * Fetch a listed posts(only containing meta properties), * such as the posts belonging to the same tag/category/topic. * @param {string} listId - id of tag, category or category_set * @param {string} listType - tag_id, category_id or category_set_id * category_set_id: `${category_id}_${subcateogry_id}` * @param {number} [limit=10] - the number of posts you want to get in one request * @param {number} [page=1] - page is used to calculate `offset`, which indicates how many posts we should skip * @param {number} timeout - request api timeout * @return {import('../typedef').Thunk} async action creator */ function fetchPostsByListId(listId, listType) { var limit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 10; var page = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : startPage; var timeout = arguments.length > 4 ? arguments[4] : undefined; var jwt = arguments.length > 5 ? arguments[5] : undefined; var isToggleBookmark = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : false; return function (dispatch, getState) { var fail = function fail(reason) { var action = { type: _actionTypes["default"].postsByListId.read.failure, payload: { listId: typeof listId === 'string' ? listId : '', error: new Error(reason) } }; dispatch(action); return Promise.reject(action); }; if (typeof page !== 'number' || isNaN(page) || page < startPage) { return fail('page should be > 0'); } if (typeof listId !== 'string' || !listId) { return fail("listId should be a string and not empty, but got ".concat(listId)); } var state = getState(); var list = _.get(state, [_reduxStateFieldNames["default"].lists, listId]); // items of page are already fetched if (Array.isArray(_.get(list, ['pages', page]))) { var action = { type: _actionTypes["default"].postsByListId.read.alreadyExists, payload: { listId: listId, limit: limit, page: page } }; dispatch(action); return Promise.resolve(action); } var offset = (page - 1) * limit; var apiOrigin = _.get(state, [_reduxStateFieldNames["default"].origins, 'api']); var path = "/v2/".concat(_apiEndpoints["default"].posts); var params = { limit: limit, offset: offset }; if (listType === 'latest') { params.sort = '-published_date'; } else if (listType === 'category_set_id') { var _$split = _.split(listId, '_'), _$split2 = _slicedToArray(_$split, 2), categoryId = _$split2[0], subcategoryId = _$split2[1]; params.category_id = categoryId; params.subcategory_id = subcategoryId; } else { params[listType] = listId; } if (isToggleBookmark) { params.toggleBookmark = 1; } var url = (0, _url.formURL)(apiOrigin, path, params); dispatch({ type: _actionTypes["default"].postsByListId.read.request, payload: { url: url, listId: listId } }); return _fetchPosts(dispatch, url, _actionTypes["default"].postsByListId.read.success, _actionTypes["default"].postsByListId.read.failure, { listId: listId, page: page }, timeout, jwt); }; } /** * Fetch posts(only containing meta properties) by category list id. * @param {string} listId - id of category * @param {number} [limit=10] - the number of posts you want to get in one request * @param {number} [page=1] - page is used to calculate `offset`, which indicates how many posts we should skip * @param {number} [timeout=apiConfig.timeout] - request api timeout * @return {import('../typedef').Thunk} async action creator */ function fetchPostsByCategoryListId(listId) { var limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10; var page = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var timeout = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _apiConfig["default"].timeout; return function (dispatch, getState) { return fetchPostsByListId(listId, 'category_id', limit, page, timeout)(dispatch, getState); }; } /** * Fetch posts(only containing meta properties) by tag list id. * @param {string} listId - id of tag * @param {number} [limit=10] - the number of posts you want to get in one request * @param {number} [page=1] - page is used to calculate `offset`, which indicates how many posts we should skip * @param {number} [timeout=apiConfig.timeout] - request api timeout * @return {import('../typedef').Thunk} async action creator */ function fetchPostsByTagListId(listId) { var limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10; var page = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var jwt = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; var toggleBookmark = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false; var timeout = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : _apiConfig["default"].timeout; return function (dispatch, getState) { return fetchPostsByListId(listId, 'tag_id', limit, page, timeout, jwt, toggleBookmark)(dispatch, getState); }; } /** * Fetch posts(only containing meta properties) by category set id. * @param {string} listId - id of category set * category set id: `${category_id}_${subcategory_id}` * @param {number} [limit=10] - the number of posts you want to get in one request * @param {number} [page=1] - page is used to calculate `offset`, which indicates how many posts we should skip * @param {number} [timeout=apiConfig.timeout] - request api timeout * @return {import('../typedef').Thunk} async action creator */ function fetchPostsByCategorySetListId(listId) { var limit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 10; var page = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var timeout = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _apiConfig["default"].timeout; return function (dispatch, getState) { return fetchPostsByListId(listId, 'category_set_id', limit, page, timeout)(dispatch, getState); }; } /** * Fetch latest posts(only containing meta properties) sorted by published date. * @param {number} [limit=10] - the number of posts you want to get in one request * @param {number} [page=1] - page is used to calculate `offset`, which indicates how many posts we should skip * @param {number} [timeout=apiConfig.timeout] - request api timeout * @return {import('../typedef').Thunk} async action creator * */ function fetchLatestPosts() { var limit = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 10; var page = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var jwt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; var toggleBookmark = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var timeout = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : _apiConfig["default"].timeout; return function (dispatch, getState) { return fetchPostsByListId(_latest.LATEST_LIST_ID, 'latest', limit, page, timeout, jwt, toggleBookmark)(dispatch, getState); }; }