@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
289 lines (280 loc) • 16.3 kB
JavaScript
import { __rest } from "tslib";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useEffect, useMemo, useRef, useState } from 'react';
import { styled } from '@mui/material/styles';
import CommentNotification from '../Notification/Comment';
import UserFollowNotification from '../Notification/UserFollow';
import UndeletedForNotification from '../Notification/UndeletedFor';
import DeletedForNotification from '../Notification/DeletedFor';
import UserConnectionNotification from '../Notification/UserConnection';
import PrivateMessageNotification from '../Notification/PrivateMessage';
import UserBlockedNotification from '../Notification/UserBlocked';
import MentionNotification from '../Notification/Mention';
import CollapsedForNotification from '../Notification/CollapsedFor';
import KindlyNoticeForNotification from '../Notification/KindlyNoticeFor';
import KindlyNoticeFlagNotification from '../Notification/KindlyNoticeFlag';
import VoteUpNotification from '../Notification/VoteUp';
import { SCOPE_SC_UI } from '../../constants/Errors';
import PubSub from 'pubsub-js';
import ContributionFollowNotification from '../Notification/ContributionFollow';
import { Avatar, Box, MenuItem, MenuList, Typography } from '@mui/material';
import IncubatorApprovedNotification from '../Notification/IncubatorApproved';
import classNames from 'classnames';
import Skeleton from './Skeleton';
import { SCNotificationObjectTemplateType } from '../../types';
import ScrollContainer from '../../shared/ScrollContainer';
import { FormattedMessage, useIntl } from 'react-intl';
import { SCNotificationTopicType, SCNotificationTypologyType } from '@selfcommunity/types';
import { Endpoints, http } from '@selfcommunity/api-services';
import { Logger } from '@selfcommunity/utils';
import { Link, SCNotification, SCPreferences, SCRoutes, useSCPreferences, useSCRouting, useSCUser } from '@selfcommunity/react-core';
import { useThemeProps } from '@mui/system';
import ContributionNotification from '../Notification/Contribution';
import NotificationItem from '../../shared/NotificationItem';
import { PREFIX } from './constants';
import GroupNotification from '../Notification/Group';
import EventNotification from '../Notification/Event/Event';
import LiveStreamNotification from '../Notification/LiveStream';
const classes = {
root: `${PREFIX}-root`,
notificationsWrap: `${PREFIX}-notifications-wrap`,
emptyBoxNotifications: `${PREFIX}-empty-box-notifications`,
list: `${PREFIX}-list`,
broadcastMessagesBanner: `${PREFIX}-broadcast-messages-banner`,
item: `${PREFIX}-item`
};
const Root = styled(Box, {
name: PREFIX,
slot: 'Root'
})(() => ({}));
const PREFERENCES = [SCPreferences.LOGO_NAVBAR_LOGO_MOBILE, SCPreferences.TEXT_APPLICATION_NAME];
/**
* > API documentation for the Community-JS SnippetNotifications component. Learn about the available props and the CSS API.
*
*
* This component renders the notification list.
* Take a look at our <strong>demo</strong> component [here](/docs/sdk/community-js/react-ui/Components/SnippetNotifications)
#### Import
```jsx
import {SnippetNotifications} from '@selfcommunity/react-ui';
```
#### Component Name
The name `SCSnippetNotifications` can be used when providing style overrides in the theme.
#### CSS
|Rule Name|Global class|Description|
|---|---|---|
|root|.SCSnippetNotification-root|Styles applied to the root element.|
|notificationsWrap|.SCSnippetNotification-notification-wrap|Styles applied to the notifications wrap.|
|emptyBoxNotifications|.SCSnippetNotification-empty-box-notifications|Styles applied to the box indicating that there are no notifications.|
|list|.SCSnippetNotification-list|Styles applied to the list of notifications.|
|item|.SCSnippetNotification-item|Styles applied to the single notification.|
|broadcastMessagesBanner|.SCSnippetNotification-broadcast-messages-banner|Styles applied to the broadcast message banner.|
* @param inProps
*/
export default function SnippetNotifications(inProps) {
// PROPS
const props = useThemeProps({
props: inProps,
name: PREFIX
});
const { id = `snippetNotifications`, className, showMax = 20, handleCustomNotification, handleNotification, ScrollContainerProps = {}, onNotificationClick, onFetchNotifications } = props, rest = __rest(props, ["id", "className", "showMax", "handleCustomNotification", "handleNotification", "ScrollContainerProps", "onNotificationClick", "onFetchNotifications"]);
// CONTEXT
const scUserContext = useSCUser();
const scRoutingContext = useSCRouting();
// STATE
const [notifications, setNotifications] = useState([]);
const [initialized, setInitialized] = useState(false);
const [loading, setLoading] = useState(false);
// REFS
const notificationSubscription = useRef(null);
// Compute preferences
const scPreferences = useSCPreferences();
const preferences = useMemo(() => {
const _preferences = {};
PREFERENCES.map((p) => (_preferences[p] = p in scPreferences.preferences ? scPreferences.preferences[p].value : null));
return _preferences;
}, [scPreferences.preferences]);
// HOOKS
const intl = useIntl();
/**
* Perform vote
*/
const performFetchNotifications = useMemo(() => () => {
return http
.request({
url: Endpoints.UserNotificationList.url(),
method: Endpoints.UserNotificationList.method
})
.then((res) => {
if (res.status >= 300) {
return Promise.reject(res);
}
return Promise.resolve(res.data);
});
}, []);
/**
* Handles vote
* @param comment
*/
const fetchNotifications = useMemo(() => () => {
setInitialized(true);
if (!loading) {
setLoading(true);
performFetchNotifications()
.then((data) => {
setNotifications(data.results);
setLoading(false);
scUserContext.refreshCounters();
onFetchNotifications && onFetchNotifications(data.results);
})
.catch((error) => {
Logger.error(SCOPE_SC_UI, error);
});
}
}, [loading, notifications.length, onFetchNotifications, setInitialized]);
/**
* Fetch notifications
*/
useEffect(() => {
let _t;
if (scUserContext.user && !initialized) {
_t = setTimeout(fetchNotifications);
return () => {
_t && clearTimeout(_t);
};
}
}, [scUserContext.user, initialized]);
/**
* Notification subscriber
* @param msg
* @param data
*/
const notificationSubscriber = (msg, data) => {
/**
* Ignore notifications of type: notification_banner
* (data.data.activity_type === SCNotificationTypologyType.NOTIFICATION_BANNER)
*/
if (data &&
data.type === SCNotificationTopicType.INTERACTION &&
SCNotification.SCNotificationMapping[data.data.activity_type] &&
!SCNotification.SCSilentSnippetNotifications.includes(data.data.activity_type)) {
if (data.data.notification_obj) {
setNotifications([...[{ is_new: true, sid: '', aggregated: [data.data.notification_obj] }], ...notifications]);
}
}
};
const handleSingleNotificationClick = (e, n) => {
if (onNotificationClick) {
onNotificationClick(e, n);
}
};
/**
* On mount, fetches first page of notifications
* On mount, subscribe to receive notification updates
*/
useEffect(() => {
if (!loading) {
notificationSubscription.current = PubSub.subscribe(SCNotificationTopicType.INTERACTION, notificationSubscriber);
}
return () => {
notificationSubscription.current && PubSub.unsubscribe(notificationSubscription.current);
};
}, [loading]);
/**
* Render every single notification in aggregated group
* @param n
* @param i
*/
const renderAggregatedItem = (n, i) => {
const type = n.type;
let content;
if (type === SCNotificationTypologyType.COMMENT || type === SCNotificationTypologyType.NESTED_COMMENT) {
content = _jsx(CommentNotification, { notificationObject: n, index: i, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.FOLLOW) {
content = _jsx(ContributionFollowNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.USER_FOLLOW) {
content = _jsx(UserFollowNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.CONNECTION_REQUEST || type === SCNotificationTypologyType.CONNECTION_ACCEPT) {
content = _jsx(UserConnectionNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.VOTE_UP) {
content = _jsx(VoteUpNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.KINDLY_NOTICE_ADVERTISING ||
type === SCNotificationTypologyType.KINDLY_NOTICE_AGGRESSIVE ||
type === SCNotificationTypologyType.KINDLY_NOTICE_POOR ||
type === SCNotificationTypologyType.KINDLY_NOTICE_VULGAR ||
type === SCNotificationTypologyType.KINDLY_NOTICE_OFFTOPIC) {
content = _jsx(KindlyNoticeForNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.KINDLY_NOTICE_FLAG) {
content = _jsx(KindlyNoticeFlagNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.DELETED_FOR_ADVERTISING ||
type === SCNotificationTypologyType.DELETED_FOR_AGGRESSIVE ||
type === SCNotificationTypologyType.DELETED_FOR_POOR ||
type === SCNotificationTypologyType.DELETED_FOR_VULGAR ||
type === SCNotificationTypologyType.DELETED_FOR_OFFTOPIC) {
content = _jsx(DeletedForNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.UNDELETED_FOR) {
content = _jsx(UndeletedForNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.COLLAPSED_FOR_ADVERTISING ||
type === SCNotificationTypologyType.COLLAPSED_FOR_AGGRESSIVE ||
type === SCNotificationTypologyType.COLLAPSED_FOR_POOR ||
type === SCNotificationTypologyType.COLLAPSED_FOR_VULGAR ||
type === SCNotificationTypologyType.COLLAPSED_FOR_OFFTOPIC) {
content = _jsx(CollapsedForNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.PRIVATE_MESSAGE) {
content = _jsx(PrivateMessageNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.BLOCKED_USER || type === SCNotificationTypologyType.UNBLOCKED_USER) {
content = _jsx(UserBlockedNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.MENTION) {
content = _jsx(MentionNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.INCUBATOR_APPROVED) {
content = _jsx(IncubatorApprovedNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.CUSTOM_NOTIFICATION && handleCustomNotification) {
content = handleCustomNotification(n);
}
else if (type === SCNotificationTypologyType.CONTRIBUTION) {
content = _jsx(ContributionNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (n.type === SCNotificationTypologyType.USER_ADDED_TO_GROUP ||
n.type === SCNotificationTypologyType.USER_INVITED_TO_JOIN_GROUP ||
n.type === SCNotificationTypologyType.USER_ACCEPTED_TO_JOIN_GROUP ||
n.type === SCNotificationTypologyType.USER_REQUESTED_TO_JOIN_GROUP) {
return _jsx(GroupNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (n.type === SCNotificationTypologyType.USER_ADDED_TO_EVENT ||
n.type === SCNotificationTypologyType.USER_INVITED_TO_JOIN_EVENT ||
n.type === SCNotificationTypologyType.USER_ACCEPTED_TO_JOIN_EVENT ||
n.type === SCNotificationTypologyType.USER_REQUESTED_TO_JOIN_EVENT) {
return _jsx(EventNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
else if (type === SCNotificationTypologyType.LIVE_STREAM_STARTED) {
content = _jsx(LiveStreamNotification, { notificationObject: n, template: SCNotificationObjectTemplateType.SNIPPET }, i);
}
if (type && handleNotification) {
/** Override content */
content = handleNotification(type, n, content);
}
return content;
};
/**
* Renders root object
*/
return (_jsx(Root, Object.assign({ id: id, className: classNames(classes.root, className) }, rest, { children: _jsx(Box, Object.assign({ className: classes.notificationsWrap }, { children: !initialized || loading ? (_jsx(Skeleton, { elevation: 0 })) : (_jsx(ScrollContainer, Object.assign({}, ScrollContainerProps, { children: _jsxs(MenuList, Object.assign({ className: classes.list, disabledItemsFocusable: true, disableListWrap: true }, { children: [scUserContext.user.unseen_notification_banners_counter ? (_jsx(MenuItem, Object.assign({ className: classNames(classes.item, classes.broadcastMessagesBanner), component: Link, to: scRoutingContext.url(SCRoutes.USER_NOTIFICATIONS_ROUTE_NAME, {}) }, { children: _jsx(NotificationItem, { template: SCNotificationObjectTemplateType.SNIPPET, isNew: true, disableTypography: true, image: _jsx(Avatar, { alt: preferences[SCPreferences.TEXT_APPLICATION_NAME], src: preferences[SCPreferences.LOGO_NAVBAR_LOGO_MOBILE] }), primary: _jsx(Typography, Object.assign({ component: 'div' }, { children: intl.formatMessage({ id: 'ui.snippetNotifications.broadcastMessages', defaultMessage: 'ui.snippetNotifications.broadcastMessages' }, {
count: scUserContext.user.unseen_notification_banners_counter,
b: (...chunks) => _jsx("strong", { children: chunks }),
link: (...chunks) => _jsx(Link, Object.assign({ to: scRoutingContext.url(SCRoutes.USER_NOTIFICATIONS_ROUTE_NAME, {}) }, { children: chunks }))
}) })) }) }), "banner")) : null, notifications.length === 0 ? (_jsx(MenuItem, Object.assign({ className: classes.emptyBoxNotifications }, { children: _jsx(FormattedMessage, { id: "ui.snippetNotifications.noNotifications", defaultMessage: "ui.snippetNotifications.noNotifications" }) }))) : (notifications.slice(0, showMax).map((notificationObject, i) => notificationObject.aggregated.map((n, k) => (_jsx(MenuItem, Object.assign({ className: classes.item, onClick: (e) => handleSingleNotificationClick(e, n), disableRipple: true, disableTouchRipple: true }, { children: renderAggregatedItem(n, i) }), k)))))] })) }))) })) })));
}