UNPKG

@selfcommunity/react-ui

Version:

React UI Components to integrate a Community created with SelfCommunity Platform.

199 lines (189 loc) • 10.2 kB
import { __rest } from "tslib"; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import React, { useContext, useState } from 'react'; import Widget from '../Widget'; import { FormattedMessage } from 'react-intl'; import { Avatar, Box, CardContent, Typography, styled } from '@mui/material'; import Bullet from '../../shared/Bullet'; import classNames from 'classnames'; import { SCOPE_SC_UI } from '../../constants/Errors'; import CommentObjectSkeleton from './Skeleton'; import CommentObjectReply from '../CommentObjectReply'; import DateTimeAgo from '../../shared/DateTimeAgo'; import { getCommentContributionHtml } from '../../utils/contribution'; import { useSnackbar } from 'notistack'; import { useThemeProps } from '@mui/system'; import BaseItem from '../../shared/BaseItem'; import { Endpoints, http } from '@selfcommunity/api-services'; import { CacheStrategies, Logger, LRUCache } from '@selfcommunity/utils'; import { Link, SCCache, SCRoutes, SCUserContext, UserUtils, useSCFetchLessonCommentObject, useSCRouting } from '@selfcommunity/react-core'; import UserDeletedSnackBar from '../../shared/UserDeletedSnackBar'; import UserAvatar from '../../shared/UserAvatar'; import { PREFIX } from './constants'; import LessonCommentActionsMenu from '../../shared/LessonCommentActionsMenu'; import LessonFilePreview from '../../shared/LessonFilePreview'; const classes = { root: `${PREFIX}-root`, comment: `${PREFIX}-comment`, avatar: `${PREFIX}-avatar`, content: `${PREFIX}-content`, author: `${PREFIX}-author`, textContent: `${PREFIX}-text-content`, mediaContent: `${PREFIX}-media-content`, commentActionsMenu: `${PREFIX}-comment-actions-menu` }; const Root = styled(Box, { name: PREFIX, slot: 'Root' })(() => ({})); /** * > API documentation for the Community-JS Comment Object component. Learn about the available props and the CSS API. * * * This component renders a comment item. * Take a look at our <strong>demo</strong> component [here](/docs/sdk/community-js/react-ui/Components/CommentObject) #### Import ```jsx import {CommentObject} from '@selfcommunity/react-ui'; ``` #### Component Name The name `SCCommentObject` can be used when providing style overrides in the theme. #### CSS |Rule Name|Global class|Description| |---|---|---| |root|.SCCommentObject-root|Styles applied to the root element.| |comment|.SCCommentObject-comment|Styles applied to comment element.| |avatar|.SCCommentObject-avatar|Styles applied to the avatar element.| |author|.SCCommentObject-author|Styles applied to the author section.| |content|.SCCommentObject-content|Styles applied to content section.| |textContent|.SCCommentObject-text-content|Styles applied to text content section.| |mediaContent|.SCCommentObject-media-content|Styles applied to media content section.| |commentActionsMenu|.SCCommentObject-comment-actions-menu|Styles applied to comment action menu element.| * @param inProps */ export default function LessonCommentObject(inProps) { // PROPS const props = useThemeProps({ props: inProps, name: PREFIX }); const { id = `lesson_comment_object_${props.commentObjectId ? props.commentObjectId : props.commentObject ? props.commentObject.id : ''}`, className, commentObjectId, commentObject, isEditing, lessonObjectId, lessonObject, onDelete, elevation = 0, CommentObjectSkeletonProps = { elevation }, CommentObjectReplyProps = { elevation }, cacheStrategy = CacheStrategies.NETWORK_ONLY } = props, rest = __rest(props, ["id", "className", "commentObjectId", "commentObject", "isEditing", "lessonObjectId", "lessonObject", "onDelete", "elevation", "CommentObjectSkeletonProps", "CommentObjectReplyProps", "cacheStrategy"]); // CONTEXT const scUserContext = useContext(SCUserContext); const scRoutingContext = useSCRouting(); const { enqueueSnackbar } = useSnackbar(); // STATE const { obj, setObj } = useSCFetchLessonCommentObject({ id: commentObjectId, commentObject: commentObject, lesson: lessonObject, cacheStrategy }); const [isSavingComment, setIsSavingComment] = useState(false); const [editComment, setEditComment] = useState(null); const [openAlert, setOpenAlert] = useState(false); // HANDLERS /** * Update state object * @param newObj */ function updateObject(newObj) { LRUCache.set(SCCache.getLessonCommentCacheKey(obj.id), newObj); setObj(newObj); LRUCache.deleteKeysWithPrefix(SCCache.getLessonCommentsCachePrefixKeys(lessonObject.id)); } /** * Handle comment delete */ function handleDelete(comment) { updateObject(comment); onDelete && onDelete(comment); } /** * Handle edit comment */ function handleEdit(comment) { setEditComment(comment); isEditing && isEditing(true); } function handleCancel() { setEditComment(null); isEditing && isEditing(false); } /** * Perform save/update comment */ const performUpdate = (comment, medias) => { const mediaIds = medias ? medias.map((media) => media.id) : []; return http .request({ url: Endpoints.UpdateCourseComment.url({ id: lessonObject.course_id, section_id: lessonObject.section_id, lesson_id: lessonObject.id, comment_id: commentObject.id }), method: Endpoints.UpdateCourseComment.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 handleUpdate(comment, medias) { if (UserUtils.isBlocked(scUserContext.user)) { enqueueSnackbar(_jsx(FormattedMessage, { id: "ui.common.userBlocked", defaultMessage: "ui.common.userBlocked" }), { variant: 'warning', autoHideDuration: 3000 }); } else { setIsSavingComment(true); performUpdate(comment, medias) .then((data) => { const newObj = Object.assign({}, obj, { text: data.text, html: data.html, created_at: data.created_at, medias: medias }); updateObject(newObj); setEditComment(null); setIsSavingComment(false); isEditing && isEditing(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 }); }); } } /** * Render comment & latest activities * @param comment */ function renderComment(comment) { const summaryHtml = getCommentContributionHtml(comment.html, scRoutingContext.url); return (_jsx(React.Fragment, { children: editComment && editComment.id === comment.id ? (_jsx(Box, Object.assign({ className: classes.comment }, { children: _jsx(CommentObjectReply, Object.assign({ text: comment.html, medias: comment.medias, autoFocus: true, id: `edit-${comment.id}`, onSave: handleUpdate, onCancel: handleCancel, editable: !isSavingComment, EditorProps: { uploadFile: true, uploadImage: false, isLessonCommentEditor: true } }, CommentObjectReplyProps)) }))) : (_jsx(BaseItem, { elevation: 0, className: classes.comment, image: _jsx(Link, Object.assign({}, (!comment.created_by.deleted && { to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, comment.created_by) }), { onClick: comment.created_by.deleted ? () => setOpenAlert(true) : null }, { children: _jsx(UserAvatar, Object.assign({ hide: !obj.created_by.community_badge }, { children: _jsx(Avatar, { alt: obj.created_by.username, variant: "circular", src: comment.created_by.avatar, className: classes.avatar }) })) })), disableTypography: true, primary: _jsx(_Fragment, { children: _jsxs(Widget, Object.assign({ className: classes.content, elevation: elevation }, rest, { children: [_jsxs(CardContent, { children: [_jsx(Link, Object.assign({ className: classes.author }, (!comment.created_by.deleted && { to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, comment.created_by) }), { onClick: comment.created_by.deleted ? () => setOpenAlert(true) : null }, { children: _jsx(Typography, Object.assign({ component: "span" }, { children: comment.created_by.username })) })), _jsxs(_Fragment, { children: [_jsx(Bullet, {}), _jsx(DateTimeAgo, { date: comment.created_at, showStartIcon: false })] }), _jsx(Typography, { className: classes.textContent, variant: "body2", gutterBottom: true, dangerouslySetInnerHTML: { __html: summaryHtml } }), obj.medias && obj.medias.length > 0 && (_jsx(_Fragment, { children: obj.medias.map((media) => { return _jsx(LessonFilePreview, { className: classes.mediaContent, media: media }, media.id); }) }))] }), scUserContext.user && scUserContext.user.id === comment.created_by.id && (_jsx(Box, Object.assign({ className: classes.commentActionsMenu }, { children: _jsx(LessonCommentActionsMenu, { lesson: lessonObject, commentObject: comment, onDelete: handleDelete, onEdit: handleEdit }) })))] })) }) })) }, comment.id)); } /** * Render comments */ let comment; if (obj) { comment = renderComment(obj); } else { comment = _jsx(CommentObjectSkeleton, Object.assign({}, CommentObjectSkeletonProps)); } /** * Render object */ return (_jsxs(_Fragment, { children: [_jsx(Root, Object.assign({ id: id, className: classNames(classes.root, className) }, { children: comment })), openAlert && _jsx(UserDeletedSnackBar, { open: openAlert, handleClose: () => setOpenAlert(false) })] })); }