@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
182 lines (174 loc) • 8.7 kB
JavaScript
import { __rest } from "tslib";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useRef, useState } from 'react';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import LessonCommentObject, { LessonCommentObjectSkeleton } from '../LessonCommentObject';
import { Box, List, ListItem, styled } from '@mui/material';
import classNames from 'classnames';
import { useThemeProps } from '@mui/system';
import { SCCommentsOrderBy } from '@selfcommunity/types';
import { CacheStrategies, Logger, LRUCache } from '@selfcommunity/utils';
import { SCCache, UserUtils, useSCFetchLessonCommentObjects, useSCUser } from '@selfcommunity/react-core';
import { PREFIX } from './constants';
import LessonCommentsObjectSkeleton from './Skeleton';
import CommentObjectReply from '../CommentObjectReply';
import { enqueueSnackbar } from 'notistack';
import { SCOPE_SC_UI } from '../../constants/Errors';
import { Endpoints, http } from '@selfcommunity/api-services';
import InfiniteScroll from '../../shared/InfiniteScroll';
import HiddenPlaceholder from '../../shared/HiddenPlaceholder';
const messages = defineMessages({
commentEditorPlaceholder: {
id: 'ui.lessonCommentObjects.editor.placeholder',
defaultMessage: 'ui.lessonCommentObjects.editor.placeholder'
}
});
const classes = {
root: `${PREFIX}-root`,
loadNextCommentsButton: `${PREFIX}-load-more-comments-button`,
loadPreviousCommentsButton: `${PREFIX}-load-previous-comments-button`,
paginationLink: `${PREFIX}-pagination-link`,
pagination: `${PREFIX}-pagination`,
commentsCounter: `${PREFIX}-comments-counter`,
commentNotFound: `${PREFIX}-comment-not-found`,
noOtherComments: `${PREFIX}-no-other-comment`
};
const Root = styled(Box, {
name: PREFIX,
slot: 'Root'
})(() => ({}));
/**
* > API documentation for the Community-JS Comments Object component. Learn about the available props and the CSS API.
#### Import
```jsx
import {LessonCommentObjects} from '@selfcommunity/react-ui';
```
#### Component Name
The name `SCLessonCommentObjects` can be used when providing style overrides in the theme.
#### CSS
|Rule Name|Global class|Description|
|---|---|---|
|root|.SCLessonCommentObjects-root|Styles applied to the root element.|
|pagination|.SCLessonCommentObjects-pagination|Styles applied to the pagination controls.|
|paginationLink|.SCLessonCommentObjects-pagination-link|Styles applied to the pagination link.|
|loadNextCommentsButton|.SCLessonCommentObjects-load-next-comments-button|Styles applied to the load next comments button.|
|loadPreviousCommentsButton|.SCLessonCommentObjects-load-previous-comments-button|Styles applied to the load previous comments button.|
|commentsCounter|.SCLessonCommentObjects-comments-counter|Styles applied to the comments counter element.|
* @param inProps
*/
export default function LessonCommentObjects(inProps) {
const props = useThemeProps({
props: inProps,
name: PREFIX
});
// PROPS
const { id = `lesson_comment_objects_lesson_${props.lessonObjectId ? props.lessonObjectId : props.lessonObject ? props.lessonObject.id : ''}`, className, lessonObjectId, lessonObject, CommentComponentProps = {}, CommentObjectSkeletonProps = { elevation: 0 }, CommentsObjectSkeletonProps = {}, cacheStrategy = CacheStrategies.STALE_WHILE_REVALIDATE } = props, rest = __rest(props, ["id", "className", "lessonObjectId", "lessonObject", "CommentComponentProps", "CommentObjectSkeletonProps", "CommentsObjectSkeletonProps", "cacheStrategy"]);
//STATE
const [commenting, setIsCommenting] = useState(false);
const [editing, setIsEditing] = useState(false);
const [replyKey, setReplyKey] = useState(0);
// CONTEXT
const scUserContext = useSCUser();
// INTL
const intl = useIntl();
// REF
const commentsEndRef = useRef(null);
// STATE
const commentsObject = useSCFetchLessonCommentObjects({
id: lessonObject.id,
lessonObject: lessonObject,
orderBy: SCCommentsOrderBy.ADDED_AT_ASC,
cacheStrategy
});
// EFFECTS
useEffect(() => {
if (commentsObject.lessonObject) {
commentsObject.getNextPage();
}
}, [commentsObject.lessonObject]);
//HANDLERS
function handleNext() {
commentsObject.getNextPage();
}
const scrollToBottom = () => {
var _a;
(_a = commentsEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'end', behavior: 'smooth' });
};
useEffect(() => {
if (commentsObject.comments.length > 0) {
scrollToBottom();
}
}, [commentsObject.comments]);
/**
* Perform save/update comment
*/
const performComment = (comment, medias) => {
const mediaIds = medias ? medias.map((media) => media.id) : [];
return http
.request({
url: Endpoints.CreateCourseComment.url({ id: lessonObject.course_id, section_id: lessonObject.section_id, lesson_id: lessonObject.id }),
method: Endpoints.CreateCourseComment.method,
data: { text: comment, medias: mediaIds }
})
.then((res) => {
if (res.status >= 300) {
return Promise.reject(res);
}
return Promise.resolve(res.data);
});
};
/**
* Handle save comment
*/
function handleCommentAction(comment, medias) {
if (UserUtils.isBlocked(scUserContext.user)) {
enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.common.userBlocked", defaultMessage: "ui.common.userBlocked" }), {
variant: 'warning',
autoHideDuration: 3000
});
}
else {
setIsCommenting(true);
performComment(comment, medias)
.then((data) => {
handleCommentsUpdate(data);
setReplyKey(comment.id);
setIsCommenting(false);
})
.catch((error) => {
Logger.error(SCOPE_SC_UI, error);
enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.common.error.action", defaultMessage: "ui.common.error.action" }), {
variant: 'error',
autoHideDuration: 3000
});
});
}
}
const handleCommentsUpdate = (comment, forDeletion) => {
let updated;
if (forDeletion) {
updated = commentsObject.comments.filter((c) => c.id !== comment.id);
}
else {
updated = [...commentsObject.comments, comment];
}
commentsObject.updateLessonComments([...updated]);
LRUCache.set(SCCache.getLessonCommentCacheKey(lessonObject.id), updated);
LRUCache.deleteKeysWithPrefix(SCCache.getLessonCommentsCachePrefixKeys(lessonObject.id));
};
/**
* Renders root object(if obj)
*/
if (!commentsObject.lessonObject) {
return _jsx(HiddenPlaceholder, {});
}
if (!commentsObject.comments.length && commentsObject.isLoadingNext) {
return _jsx(LessonCommentsObjectSkeleton, Object.assign({ count: 5 }, CommentsObjectSkeletonProps));
}
return (_jsx(Root, Object.assign({ id: id, className: classNames(classes.root, className) }, rest, { children: _jsxs(_Fragment, { children: [_jsx(_Fragment, { children: commentsObject.comments.length > 0 ? (_jsx(InfiniteScroll, Object.assign({ height: '100%', dataLength: commentsObject.comments.length, next: handleNext, hasMoreNext: Boolean(commentsObject.next), loaderNext: _jsx(LessonCommentObjectSkeleton, Object.assign({}, CommentObjectSkeletonProps, { count: 1 })) }, { children: _jsx(List, Object.assign({ ref: commentsEndRef }, { children: commentsObject.comments.map((c, index) => (_jsx(ListItem, { children: _jsx(LessonCommentObject, Object.assign({ commentObject: c, lessonObject: commentsObject.lessonObject, onDelete: (comment) => handleCommentsUpdate(comment, true), isEditing: (editing) => setIsEditing(editing) }, CommentComponentProps, { CommentObjectSkeletonProps: CommentObjectSkeletonProps }), c.id) }, index))) })) }))) : null }), !editing && (_jsx(CommentObjectReply, { id: `reply-lessonCommentObjects`, showAvatar: false, replyIcon: true, editable: !commenting, onReply: handleCommentAction, EditorProps: {
placeholder: intl.formatMessage(messages.commentEditorPlaceholder),
uploadFile: true,
uploadImage: false,
isLessonCommentEditor: true
} }, replyKey))] }) })));
}