@selfcommunity/react-core
Version:
React Core Components useful for integrating UI Community components (react-ui).
303 lines (302 loc) • 14.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.commentsObjectActionTypes = void 0;
const tslib_1 = require("tslib");
const react_1 = require("react");
const Errors_1 = require("../constants/Errors");
const types_1 = require("@selfcommunity/types");
const api_services_1 = require("@selfcommunity/api-services");
const utils_1 = require("@selfcommunity/utils");
const Cache_1 = require("../constants/Cache");
const hooks_1 = require("../utils/hooks");
const pagination_1 = require("../utils/pagination");
const useSCFetchLesson_1 = tslib_1.__importDefault(require("../hooks/useSCFetchLesson"));
/**
* @hidden
* We have complex state logic that involves multiple sub-values,
* so useReducer is preferable to useState.
* Define all possible auth action types label
* Use this to export actions and dispatch an action
*/
exports.commentsObjectActionTypes = {
LOADING_NEXT: '_loading_next',
LOADING_PREVIOUS: '_loading_previous',
DATA_NEXT_LOADED: '_data_next_loaded',
DATA_PREVIOUS_LOADED: '_data_previous_loaded',
DATA_RELOAD: '_data_reload',
DATA_RELOADED: '_data_reloaded',
DATA_REVALIDATE: '_data_revalidate',
};
/**
* commentsReducer:
* - manage the state of comments object
* - update the state base on action type
* @param state
* @param action
*/
function commentsReducer(state, action) {
switch (action.type) {
case exports.commentsObjectActionTypes.LOADING_NEXT:
return Object.assign(Object.assign({}, state), { isLoadingNext: true, isLoadingPrevious: false });
case exports.commentsObjectActionTypes.LOADING_PREVIOUS:
return Object.assign(Object.assign({}, state), { isLoadingNext: false, isLoadingPrevious: true });
case exports.commentsObjectActionTypes.DATA_NEXT_LOADED:
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, state), { comments: action.payload.comments, isLoadingNext: false, componentLoaded: true, revalidate: false, revalidateNext: null, revalidatePrevious: null, next: action.payload.next }), (action.payload.page ? { page: action.payload.page } : {})), (action.payload.nextPage ? { nextPage: action.payload.nextPage } : {})), (action.payload.previous ? { previous: action.payload.previous } : {})), (action.payload.previousPage ? { previousPage: action.payload.previousPage } : {})), (action.payload.total ? { total: action.payload.total } : {}));
case exports.commentsObjectActionTypes.DATA_PREVIOUS_LOADED:
return Object.assign(Object.assign(Object.assign(Object.assign({}, state), { comments: action.payload.comments, isLoadingPrevious: false, revalidate: false, revalidateNext: null, revalidatePrevious: null, previous: action.payload.previous }), (action.payload.page ? { page: action.payload.page } : {})), (action.payload.previousPage ? { previousPage: action.payload.previousPage } : {}));
case exports.commentsObjectActionTypes.DATA_RELOAD:
return Object.assign(Object.assign({}, state), { next: action.payload.next, previousPage: null, nextPage: null, comments: [], total: 0, previous: null, isLoadingNext: true, reload: true });
case exports.commentsObjectActionTypes.DATA_RELOADED:
return Object.assign(Object.assign({}, state), { componentLoaded: true, reload: false });
case exports.commentsObjectActionTypes.DATA_REVALIDATE:
return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, state), { componentLoaded: true, revalidate: true }), (action.payload.revalidateNext ? { revalidateNext: action.payload.revalidateNext } : {})), (action.payload.revalidatePrevious ? { revalidatePrevious: action.payload.revalidatePrevious } : {})), { reload: false });
default:
throw new Error(`Unhandled type: ${action.type}`);
}
}
/**
* Define initial state
* @param data
*/
function stateInitializer(data) {
const __commentsObjectCacheKey = data.scLesson ? (0, Cache_1.getLessonCommentsCacheKey)(data.scLesson.id, data.next) : null;
let _initState = {
componentLoaded: false,
comments: [],
total: 0,
next: data.next,
previous: null,
isLoadingNext: false,
isLoadingPrevious: false,
page: Math.ceil(data.offset / data.pageSize + 1),
reload: false,
revalidate: false,
revalidateNext: null,
revalidatePrevious: null,
};
_initState['nextPage'] = _initState.next ? _initState.page + 1 : null;
_initState['previousPage'] = _initState.previous ? _initState.page - 1 : null;
if (__commentsObjectCacheKey && utils_1.LRUCache.hasKey(__commentsObjectCacheKey) && data.cacheStrategy !== utils_1.CacheStrategies.NETWORK_ONLY) {
const _cachedData = utils_1.LRUCache.get(__commentsObjectCacheKey);
let page = Math.max((0, pagination_1.getCurrentPage)(_cachedData.next, data.pageSize), 1);
const nextPage = _cachedData.next ? (0, pagination_1.getCurrentPage)(_cachedData.next, data.pageSize) : null;
const previousPage = _cachedData.previous ? Math.max((0, pagination_1.getCurrentPage)(_cachedData.previous, data.pageSize) - 1, 1) : null;
return Object.assign(Object.assign({}, _initState), {
total: _cachedData.count,
next: _cachedData.next,
previous: _cachedData.previous,
comments: _cachedData.results,
page,
nextPage,
previousPage,
componentLoaded: true,
});
}
return _initState;
}
/**
:::info
This custom hooks is used to fetch paginated comments for a specific course lesson.
:::
* @param props
*/
function useSCFetchLessonCommentObjects(props) {
// PROPS
const { id, lessonObject, offset = 0, pageSize = 15, orderBy = types_1.SCCommentsOrderBy.ADDED_AT_DESC, parent, onChangePage, cacheStrategy = utils_1.CacheStrategies.NETWORK_ONLY, } = props;
// FeedObject
const { scLesson, setSCLesson } = (0, useSCFetchLesson_1.default)({
id: id,
lesson: lessonObject,
courseId: lessonObject.course_id,
sectionId: lessonObject.section_id,
cacheStrategy,
});
/**
* Get next url
*/
const getNextUrl = () => {
const _offset = offset ? `&offset=${offset}` : '';
const _parent = parent ? `&parent=${parent}` : '';
const _lessonId = scLesson ? scLesson.id : id;
const _courseId = scLesson ? scLesson.course_id : lessonObject.course_id;
const _sectionId = scLesson ? scLesson.section_id : lessonObject.section_id;
return `${api_services_1.Endpoints.GetCourseLessonComments.url({
id: _courseId,
section_id: _sectionId,
lesson_id: _lessonId,
})}?limit=${pageSize}&ordering=${orderBy}${_offset}${_parent}`;
};
// STATE
const [state, dispatch] = (0, react_1.useReducer)(commentsReducer, { scLesson, offset, pageSize, next: getNextUrl(), cacheStrategy }, stateInitializer);
// REFS
const isMountedRef = (0, hooks_1.useIsComponentMountedRef)();
/**
* Get Comments (with cache)
*/
const revalidate = (url, forward) => {
return performFetchComments(url, false).then((res) => {
let _comments;
let page = (0, pagination_1.getCurrentPage)(forward ? res.next : res.previous, pageSize);
if (forward) {
let start = state.comments.slice(0, state.comments.length - res.results.length);
_comments = start.concat(res.results);
}
else {
let start = state.comments.slice(res.results.length, state.comments.length);
_comments = res.results.concat(start);
}
if (isMountedRef.current) {
dispatch({
type: forward ? exports.commentsObjectActionTypes.DATA_NEXT_LOADED : exports.commentsObjectActionTypes.DATA_PREVIOUS_LOADED,
payload: Object.assign(Object.assign({ page, comments: _comments }, (forward ? { next: res.next } : { previous: res.previous })), { total: res.count }),
});
}
});
};
/**
* Get Comments
*/
const performFetchComments = (url, seekCache = true) => {
const __commentObjectsCacheKey = (0, Cache_1.getLessonCommentsCacheKey)(scLesson.id, url);
if (seekCache && utils_1.LRUCache.hasKey(__commentObjectsCacheKey) && cacheStrategy !== utils_1.CacheStrategies.NETWORK_ONLY) {
return Promise.resolve(utils_1.LRUCache.get(__commentObjectsCacheKey));
}
return api_services_1.http
.request({
url,
method: api_services_1.Endpoints.GetCourseLessonComments.method,
})
.then((res) => {
if (res.status >= 300) {
return Promise.reject(res);
}
utils_1.LRUCache.set(__commentObjectsCacheKey, res.data);
res.data.results.forEach((e) => utils_1.LRUCache.set((0, Cache_1.getLessonCommentCacheKey)(e.id), e));
return Promise.resolve(res.data);
});
};
/**
* Fetch previous comments
*/
function getPreviousPage() {
if (scLesson && state.previous && !state.isLoadingPrevious) {
const _previous = state.previous;
dispatch({ type: exports.commentsObjectActionTypes.LOADING_PREVIOUS });
performFetchComments(_previous)
.then((res) => {
if (isMountedRef.current) {
let currentPage = (0, pagination_1.getCurrentPage)(_previous, pageSize);
let previousPage = res.previous ? currentPage - 1 : null;
dispatch({
type: exports.commentsObjectActionTypes.DATA_PREVIOUS_LOADED,
payload: {
page: currentPage,
previousPage,
comments: [...res.results, ...state.comments],
previous: res.previous,
},
});
onChangePage && onChangePage(currentPage);
}
})
.catch((error) => {
utils_1.Logger.error(Errors_1.SCOPE_SC_CORE, error);
})
.then(() => {
if (isMountedRef.current && cacheStrategy === utils_1.CacheStrategies.STALE_WHILE_REVALIDATE) {
dispatch({ type: exports.commentsObjectActionTypes.DATA_REVALIDATE, payload: { revalidatePrevious: _previous } });
}
});
}
}
/**
* Fetch next comments
*/
function getNextPage() {
if (scLesson && state.next && !state.isLoadingNext) {
const _next = state.next;
dispatch({ type: exports.commentsObjectActionTypes.LOADING_NEXT });
performFetchComments(_next)
.then((res) => {
if (isMountedRef.current) {
let currentPage = (0, pagination_1.getCurrentPage)(_next, pageSize);
let nextPage = res.next ? currentPage + 1 : null;
dispatch({
type: exports.commentsObjectActionTypes.DATA_NEXT_LOADED,
payload: Object.assign({ page: currentPage, nextPage, comments: [...state.comments, ...res.results], next: res.next, total: res.count, componentLoaded: true }, (offset && state.comments.length === 0 ? { previous: res.previous, previousPage: res.previous ? currentPage - 1 : null } : {})),
});
onChangePage && onChangePage(currentPage);
}
})
.catch((error) => {
utils_1.Logger.error(Errors_1.SCOPE_SC_CORE, error);
})
.then(() => {
if (isMountedRef.current && cacheStrategy === utils_1.CacheStrategies.STALE_WHILE_REVALIDATE) {
dispatch({ type: exports.commentsObjectActionTypes.DATA_REVALIDATE, payload: { revalidateNext: _next } });
}
});
}
}
/**
* Reset component status on change orderBy, pageSize, offset
*/
const reload = () => {
if (isMountedRef.current && state.componentLoaded && Boolean(scLesson) && !state.isLoadingNext && !state.reload) {
dispatch({
type: exports.commentsObjectActionTypes.DATA_RELOAD,
payload: {
next: getNextUrl(),
},
});
}
};
/**
* Reload fetch comments
*/
(0, react_1.useEffect)(() => {
if (isMountedRef.current && state.componentLoaded && state.reload && !state.isLoadingNext && !state.isLoadingPrevious) {
dispatch({
type: exports.commentsObjectActionTypes.DATA_RELOADED,
});
getNextPage();
}
}, [state.reload, isMountedRef]);
/**
* Revalidate last fetched comments
*/
(0, react_1.useEffect)(() => {
if (isMountedRef.current && state.componentLoaded && Boolean(scLesson) && !state.reload && state.revalidate) {
revalidate(state.revalidateNext, Boolean(state.revalidateNext));
}
}, [state.revalidate, isMountedRef]);
/**
* Update lesson comments list
* @param updatedData
*/
const updateLessonComments = (updatedData) => {
if (updatedData) {
dispatch({
type: exports.commentsObjectActionTypes.DATA_NEXT_LOADED,
payload: {
comments: updatedData,
next: state.next,
total: updatedData.length,
},
});
const __commentsCacheKey = (0, Cache_1.getLessonCommentsCacheKey)(scLesson.id, state.next);
utils_1.LRUCache.set(__commentsCacheKey, updatedData.map((comment) => {
const __commentCacheKey = (0, Cache_1.getLessonCommentCacheKey)(comment.id);
utils_1.LRUCache.set(__commentCacheKey, comment);
return comment.id;
}));
}
};
return Object.assign(Object.assign({ lessonObject: scLesson, setLessonObject: setSCLesson }, state), { pageSize,
getNextPage,
getPreviousPage,
orderBy,
reload,
updateLessonComments });
}
exports.default = useSCFetchLessonCommentObjects;