@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
223 lines (215 loc) • 10.6 kB
JavaScript
import { __rest } from "tslib";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useMemo, useRef, useState } from 'react';
import { styled } from '@mui/material/styles';
import { FormattedMessage } from 'react-intl';
import CommentObject from '../CommentObject';
import { Box } from '@mui/material';
import { SCCommentsOrderBy } from '../../types/comments';
import classNames from 'classnames';
import { useThemeProps } from '@mui/system';
import CommentsObject from '../CommentsObject';
import { SCOPE_SC_UI } from '../../constants/Errors';
import Typography from '@mui/material/Typography';
import { getContribution } from '../../utils/contribution';
import { Endpoints, http } from '@selfcommunity/api-services';
import { CacheStrategies, Logger } from '@selfcommunity/utils';
import { useSCFetchCommentObject, useSCFetchCommentObjects } from '@selfcommunity/react-core';
import { SCContributionType } from '@selfcommunity/types';
import { DEFAULT_PAGINATION_LIMIT } from '../../constants/Pagination';
import { PREFIX } from './constants';
const classes = {
root: `${PREFIX}-root`,
noComments: `${PREFIX}-no-comments`,
commentNotFound: `${PREFIX}-comment-not-found`
};
const Root = styled(Box, {
name: PREFIX,
slot: 'Root'
})(() => ({}));
/**
* > API documentation for the Community-JS Comments Feed Object component. Learn about the available props and the CSS API.
*
*
* This component renders a list of comment items.
* Take a look at our <strong>demo</strong> component [here](/docs/sdk/community-js/react-ui/Components/CommentsFeedObject)
#### Import
```jsx
import {CommentsFeedObject} from '@selfcommunity/react-ui';
```
#### Component Name
The name `SCCommentsFeedObject` can be used when providing style overrides in the theme.
#### CSS
|Rule Name|Global class|Description|
|---|---|---|
|root|.SCCommentsFeedObject-root|Styles applied to the root element.|
|noComments|.SCCommentsFeedObject-no-comments|Styles applied to the 'no comments' section.|
|commentNotFound|.SCCommentsFeedObject-comment-not-found|Styles applied to the label 'Comment not found'.|
* @param inProps
*/
export default function CommentsFeedObject(inProps) {
const props = useThemeProps({
props: inProps,
name: PREFIX
});
// PROPS
const { id = `comments_object_${props.feedObjectType ? props.feedObjectType : props.feedObject ? props.feedObject.type : ''}_${props.feedObjectId ? props.feedObjectId : props.feedObject ? props.feedObject.id : ''}`, className, feedObjectId, feedObject, feedObjectType = SCContributionType.POST, commentObjectId, commentObject, CommentComponent = CommentObject, CommentComponentProps = { variant: 'outlined', linkableCommentDateTime: false }, CommentObjectSkeletonProps = { elevation: 0, WidgetProps: { variant: 'outlined' } }, renderNoComments, renderCommentNotFound, page = 1, commentsPageCount = DEFAULT_PAGINATION_LIMIT, commentsOrderBy = SCCommentsOrderBy.ADDED_AT_ASC, showTitle = false, infiniteScrolling = true, onChangePage, comments = [], cacheStrategy = CacheStrategies.NETWORK_ONLY } = props, rest = __rest(props, ["id", "className", "feedObjectId", "feedObject", "feedObjectType", "commentObjectId", "commentObject", "CommentComponent", "CommentComponentProps", "CommentObjectSkeletonProps", "renderNoComments", "renderCommentNotFound", "page", "commentsPageCount", "commentsOrderBy", "showTitle", "infiniteScrolling", "onChangePage", "comments", "cacheStrategy"]);
// STATE
const commentsObject = useSCFetchCommentObjects({
id: feedObjectId,
feedObject,
feedObjectType,
offset: (page - 1) * commentsPageCount,
pageSize: commentsPageCount,
orderBy: commentsOrderBy,
onChangePage: onChangePage,
cacheStrategy
});
const [isLoading, setIsLoading] = useState(false);
const [comment, setComment] = useState(null);
const { obj: commentObj, error: errorCommentObj } = useSCFetchCommentObject({ id: commentObjectId, commentObject, cacheStrategy });
const [commentError, setCommentError] = useState(null);
// CONST
const objId = commentsObject.feedObject ? commentsObject.feedObject.id : null;
const commentObjId = commentObj ? commentObj.id : null;
// REFS
const commentsContainerRef = useRef();
/**
* Total number of comments
*/
const total = commentsObject.total + comments.length;
/**
* Render title
*/
const renderTitle = useMemo(() => () => {
if (showTitle) {
return (_jsx(Typography, Object.assign({ variant: "h6", gutterBottom: true, color: 'inherit' }, { children: _jsx(FormattedMessage, { id: "ui.commentsObject.title", defaultMessage: "ui.commentsObject.title", values: { total: total } }) })));
}
return null;
}, [total, isLoading]);
/**
* Render no comments
*/
function renderNoCommentsFound() {
return (_jsx(_Fragment, { children: renderNoComments ? (renderNoComments()) : (_jsx(Box, Object.assign({ className: classes.noComments }, { children: _jsx(FormattedMessage, { id: "ui.commentsObject.noComments", defaultMessage: "ui.commentsObject.noComments" }) }))) }));
}
/**
* Render comment not found
*/
function renderCommentNotFoundError() {
return (_jsx(_Fragment, { children: renderCommentNotFound ? (renderCommentNotFound()) : (_jsx(Box, Object.assign({ className: classes.commentNotFound }, { children: _jsx(FormattedMessage, { id: "ui.commentsObject.commentNotFound", defaultMessage: "ui.commentsObject.commentNotFound" }) }))) }));
}
/**
* Get a single comment
*/
const performFetchComment = useMemo(() => (commentId) => {
return http
.request({
url: Endpoints.Comment.url({ id: commentId }),
method: Endpoints.Comment.method
})
.then((res) => {
if (res.status >= 300) {
return Promise.reject(res);
}
return Promise.resolve(res.data);
});
}, [commentsObject.feedObject, commentObjectId, commentObj]);
/**
* Focus on comment
* @param comment
*/
function scrollToComment(comment) {
// Add (window.innerHeight / 2) to scroll
// (usually >= (topBar + offset) and in center of the screen)
setTimeout(() => {
// Get the comment inside commentsContainer
const el = commentsContainerRef.current ? commentsContainerRef.current.querySelector(`#comment_object_${comment.id}`) : null;
if (el) {
el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' });
}
}, 300);
}
/**
* Fetch a single comment
* and comment parent (if need it)
*/
function fetchComment() {
if (commentObj) {
if (commentObj.parent) {
setIsLoading(true);
performFetchComment(commentObj.parent)
.then((parent) => {
const _parent = Object.assign({}, parent);
_parent.latest_comments = [commentObj];
if (getContribution(parent).id === commentsObject.feedObject.id) {
setComment(_parent);
scrollToComment(commentObj);
}
else {
setCommentError(true);
}
setIsLoading(false);
})
.catch((error) => {
// Comment not found
setIsLoading(false);
Logger.error(SCOPE_SC_UI, error);
});
}
else {
if (getContribution(commentObj).id === commentsObject.feedObject.id) {
setComment(commentObj);
scrollToComment(commentObj);
}
else {
setCommentError(true);
}
setIsLoading(false);
}
}
else if (errorCommentObj) {
setIsLoading(false);
}
}
/**
* Prefetch comments only if obj exists
*/
useEffect(() => {
if (objId !== null && !isLoading) {
if (commentObjectId || commentObj) {
fetchComment();
}
else if (!isLoading) {
commentsObject.getNextPage();
}
}
}, [objId, commentObjId]);
/**
* Render comments
*/
let commentsRendered = _jsx(_Fragment, {});
if (commentsObject.componentLoaded && !total && !commentsObject.isLoadingNext && !isLoading) {
/**
* If comments were not found and loading is finished
* and the component was not looking for a particular
* comment render no comments message
*/
commentsRendered = renderNoCommentsFound();
}
else {
/**
* Two modes available:
* - infinite scroll
* - load pagination with load more button
* !IMPORTANT:
* the component will switch to 'load more pagination' mode automatically
* in case it needs to display a single comment
*/
commentsRendered = (_jsxs(_Fragment, { children: [Boolean((commentError || errorCommentObj) && !isLoading && !total) && renderCommentNotFoundError(), _jsx(CommentsObject, { feedObject: commentsObject.feedObject, comments: commentsObject.comments, endComments: [...(comment ? [comment] : []), ...(commentsOrderBy === SCCommentsOrderBy.ADDED_AT_ASC ? comments : [])], startComments: [...(commentsOrderBy === SCCommentsOrderBy.ADDED_AT_ASC ? [] : comments)], previous: commentsObject.previous, handlePrevious: commentsObject.getPreviousPage, isLoadingPrevious: commentsObject.isLoadingPrevious, next: commentsObject.next, isLoadingNext: commentsObject.isLoadingNext, handleNext: commentsObject.getNextPage, page: commentsObject.page, previousPage: commentsObject.previousPage, nextPage: commentsObject.nextPage, infiniteScrolling: infiniteScrolling && commentsObject.total > 0 && !comment && !comments.length, CommentComponent: CommentComponent, CommentComponentProps: CommentComponentProps, CommentObjectSkeletonProps: CommentObjectSkeletonProps })] }));
}
/**
* Renders root object
*/
return (_jsxs(Root, Object.assign({ id: id, className: classNames(classes.root, className) }, rest, { ref: commentsContainerRef }, { children: [renderTitle(), commentsRendered] })));
}