@twreporter/redux
Version:
redux actions and reducers for twreporter website
392 lines (379 loc) • 17.8 kB
JavaScript
;
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);
};
}