@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
241 lines (237 loc) • 14.5 kB
JavaScript
import { __awaiter, __rest } from "tslib";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useContext, useEffect, useMemo, useState } from 'react';
import { styled } from '@mui/material/styles';
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
import { TextField, Typography, Box, Avatar, Button, CardContent, Alert, FormGroup, AvatarGroup, List, ListItem } from '@mui/material';
import { http, Endpoints } from '@selfcommunity/api-services';
import { Logger } from '@selfcommunity/utils';
import { Link, SCPreferences, SCPreferencesContext, SCRoutes, useSCContext, useSCFetchIncubator, useSCRouting, useSCUser } from '@selfcommunity/react-core';
import BaseDialog from '../../shared/BaseDialog';
import classNames from 'classnames';
import { useThemeProps } from '@mui/system';
import Incubator from '../Incubator';
import Widget from '../Widget';
import AvatarGroupSkeleton from '../Skeleton/AvatarGroupSkeleton';
import CentralProgress from '../../shared/CentralProgress';
import InfiniteScroll from '../../shared/InfiniteScroll';
import User from '../User';
import { SCOPE_SC_UI } from '../../constants/Errors';
import Icon from '@mui/material/Icon';
import { FACEBOOK_SHARE, X_SHARE, LINKEDIN_SHARE } from '../../constants/SocialShare';
import UserDeletedSnackBar from '../../shared/UserDeletedSnackBar';
const messages = defineMessages({
intro: {
id: 'ui.incubatorDetail.intro',
defaultMessage: 'ui.incubatorDetail.intro'
}
});
const PREFIX = 'SCIncubatorDetail';
const classes = {
root: `${PREFIX}-root`,
avatar: `${PREFIX}-avatar`,
title: `${PREFIX}-title`,
author: `${PREFIX}-author`,
shareCard: `${PREFIX}-share-card`,
copyUrlForm: `${PREFIX}-copy-url-form`,
copyButton: `${PREFIX}-copy-button`,
copyText: `${PREFIX}-copy-text`,
shareSection: `${PREFIX}-share-section`,
socialShareButton: `${PREFIX}-social-share-button`,
subscribers: `${PREFIX}-subscribers`,
shareMenuIcon: `${PREFIX}-share-Menu-icon`
};
const Root = styled(BaseDialog, {
name: PREFIX,
slot: 'Root',
overridesResolver: (props, styles) => styles.root
})(({ theme }) => ({
' & .MuiCardContent-root': {
'&:last-child': {
paddingBottom: 0
}
},
[theme.breakpoints.down(500)]: {
minWidth: 300
},
[`& .${classes.avatar}`]: {
marginRight: theme.spacing(1),
marginBottom: theme.spacing(3)
},
[`& .${classes.title}`]: {
fontSize: '1rem',
whiteSpace: 'pre-line'
},
[`& .${classes.copyUrlForm}`]: {
flexDirection: 'row',
marginBottom: theme.spacing(1)
},
[`& .${classes.copyButton}`]: {
borderRadius: 0
},
[`& .${classes.copyText}`]: {
width: '80%',
'& .MuiInputBase-root': {
borderRadius: 0,
'& .MuiOutlinedInput-input': {
padding: theme.spacing(1)
}
}
},
[`& .${classes.shareSection}`]: {
display: 'flex',
gap: theme.spacing(),
marginTop: theme.spacing(),
marginBottom: theme.spacing(3)
},
[`& .${classes.socialShareButton}`]: {
marginRight: theme.spacing()
},
[`& .${classes.subscribers}`]: {
marginTop: theme.spacing(),
'& .MuiAvatar-root': {
color: theme.palette.common.white,
border: '2px solid #FFF'
}
}
}));
/**
* > API documentation for the Community-JS Incubator Detail component. Learn about the available props and the CSS API.
#### Import
```jsx
import {IncubatorDetail} from '@selfcommunity/react-ui';
```
#### Component Name
The name `SCIncubatorDetail` can be used when providing style overrides in the theme.
#### CSS
|Rule Name|Global class|Description|
|---|---|---|
|root|.SCIncubatorDetail-root|Styles applied to the root element.|
|avatar|.SCIncubatorDetail-avatar|Styles applied to the avatar element.|
|title|.SCIncubatorDetail-title|Styles applied to the title element.|
|author|.SCIncubatorDetail-author|Styles applied to the author element.|
|shareCard|.SCIncubatorDetail-share-card|Styles applied to the section card.|
|copyUrlForm|.SCIncubatorDetail-copy-url-form|Styles applied to the url copy section.|
|copyButton|.SCIncubatorDetail-copy-button|Styles applied to the copy button element.|
|copyText|.SCIncubatorDetail-copy-text|Styles applied to the text copy element.|
|shareSection|.SCIncubatorDetail-share-section|Styles applied to the social share section.|
|socialShareButton|.SCIncubatorDetail-social-share-button|Styles applied to the social share button.|
|subscribers|.SCIncubatorDetail-subscribers|Styles applied to the subscribers avatar section.|
* @param inProps
*/
export default function IncubatorDetail(inProps) {
// PROPS
const props = useThemeProps({
props: inProps,
name: PREFIX
});
const { incubator, incubatorId, className, open, onClose, IncubatorProps = {}, onSubscriptionsUpdate } = props, rest = __rest(props, ["incubator", "incubatorId", "className", "open", "onClose", "IncubatorProps", "onSubscriptionsUpdate"]);
// STATE
const [alert, setAlert] = useState(false);
const { scIncubator, setSCIncubator } = useSCFetchIncubator({ id: incubator.id, incubator });
const [loading, setLoading] = useState(true);
const [next, setNext] = useState(incubator.id || scIncubator ? `${Endpoints.GetIncubatorSubscribers.url({ id: incubator ? incubator.id : scIncubator.id })}?limit=10` : null);
const [total, setTotal] = useState(0);
const [subscribers, setSubscribers] = useState([]);
const [openSubscribersDialog, setOpenSubscribersDialog] = useState(false);
const [openAlert, setOpenAlert] = useState(false);
// CONTEXT
const scUserContext = useSCUser();
const scContext = useSCContext();
const scPreferencesContext = useContext(SCPreferencesContext);
const facebookShareEnabled = SCPreferences.ADDONS_SHARE_POST_ON_FACEBOOK_ENABLED in scPreferencesContext.preferences &&
scPreferencesContext.preferences[SCPreferences.ADDONS_SHARE_POST_ON_FACEBOOK_ENABLED].value;
const xShareEnabled = SCPreferences.ADDONS_SHARE_POST_ON_TWITTER_ENABLED in scPreferencesContext.preferences &&
scPreferencesContext.preferences[SCPreferences.ADDONS_SHARE_POST_ON_TWITTER_ENABLED].value;
const linkedinShareEnabled = SCPreferences.ADDONS_SHARE_POST_ON_LINKEDIN_ENABLED in scPreferencesContext.preferences &&
scPreferencesContext.preferences[SCPreferences.ADDONS_SHARE_POST_ON_LINKEDIN_ENABLED].value;
const scRoutingContext = useSCRouting();
const portal = scContext.settings.portal + scRoutingContext.url(SCRoutes.INCUBATOR_ROUTE_NAME, scIncubator);
const isSocialShareEnabled = facebookShareEnabled || xShareEnabled || linkedinShareEnabled;
// INTL
const intl = useIntl();
// HANDLERS
/**
* Copies incubator path on clipboard and notifies user
*/
const copy = () => __awaiter(this, void 0, void 0, function* () {
yield navigator.clipboard.writeText(portal);
setAlert(true);
});
/**
* Opens subscribers dialog
*/
function handleToggleSubscribersDialog() {
setOpenSubscribersDialog((prev) => !prev);
}
/**
* Handles incubator subscribe/unsubscribe callback
*/
function handleSubscribersUpdate(incubator, subscribed) {
let _subscribers = [];
if (subscribed) {
_subscribers = [...[scUserContext.user], ...subscribers];
}
else {
if (total < 5) {
_subscribers = [...subscribers.filter((u) => u.id !== scUserContext.user.id)];
}
else {
const _pSubscribers = subscribers.slice(0, 5).filter((u) => u.id !== scUserContext.user.id);
_subscribers = [..._pSubscribers, ...subscribers.slice(5)];
}
}
setTotal((prev) => prev + (subscribed ? 1 : -1));
setSubscribers(_subscribers);
setNext(null);
}
/**
* Handles subscription counter and subscribers update callbacks on subscribe/unsubscribe action
*/
const handleUpdates = (incubator, subscribed) => {
onSubscriptionsUpdate(incubator);
handleSubscribersUpdate(incubator, subscribed);
};
/**
* If id attempts to get the incubator by id
*/
useEffect(() => {
if (scIncubator) {
fetchSubscribers();
}
}, [scIncubator]);
/**
* Fetches incubator subscribers
*/
const fetchSubscribers = useMemo(() => () => {
if (next) {
http
.request({
url: next,
method: Endpoints.GetIncubatorSubscribers.method
})
.then((res) => {
if (res.status < 300) {
setSubscribers([...subscribers, ...res.data.results]);
setTotal(res.data['count']);
setNext(res.data['next']);
setLoading(false);
}
})
.catch((error) => {
Logger.error(SCOPE_SC_UI, error);
});
}
}, [scIncubator, next, loading]);
/**
* If not incubator object returns null
*/
if (!scIncubator) {
return null;
}
/**
* Renders root element
*/
return (_jsxs(_Fragment, { children: [_jsx(Root, Object.assign({ title: _jsxs(_Fragment, { children: [_jsx(Avatar, { className: classes.avatar, alt: scIncubator.user.avatar, src: scIncubator.user.avatar }), _jsxs(Box, { children: [_jsxs(Typography, Object.assign({ className: classes.title }, { children: [`${intl.formatMessage(messages.intro, { name: scIncubator.name })}`, " "] })), _jsx(Typography, Object.assign({ component: 'span' }, { children: _jsxs(Link, Object.assign({}, (!scIncubator.user.deleted && { to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, scIncubator.user) }), { onClick: scIncubator.user.deleted ? () => setOpenAlert(true) : null }, { children: ["@", scIncubator.user.username] })) }))] })] }), open: open, onClose: onClose, className: classNames(classes.root, className) }, rest, { children: _jsxs(Box, { children: [_jsx(Incubator, Object.assign({ elevation: 0, incubator: scIncubator, detailView: true, subscribeButtonProps: { onSubscribe: handleUpdates } }, IncubatorProps)), _jsx(Box, Object.assign({ className: classes.subscribers }, { children: loading && !scIncubator ? (_jsx(AvatarGroupSkeleton, Object.assign({}, rest))) : (_jsx(_Fragment, { children: total > 0 ? (_jsx(Button, Object.assign({ onClick: handleToggleSubscribersDialog, disabled: loading || !scIncubator }, { children: _jsxs(AvatarGroup, Object.assign({}, rest, { children: [subscribers.map((u) => (_jsx(Avatar, { alt: u.username, src: u.avatar }, u.id))), [...Array(Math.max(0, total - subscribers.length))].map((x, i // Add max to 0 to prevent creation of array with negative index during state update
) => (_jsx(Avatar, {}, i)))] })) }))) : null })) })), openSubscribersDialog && (_jsx(BaseDialog, Object.assign({ title: _jsxs(_Fragment, { children: [_jsx(FormattedMessage, { defaultMessage: "ui.incubatorDetail.subscribersSection.title", id: "ui.incubatorDetail.subscribersSection.title" }), " (", total, ")"] }), onClose: handleToggleSubscribersDialog, open: openSubscribersDialog }, { children: loading ? (_jsx(CentralProgress, { size: 50 })) : (_jsx(InfiniteScroll, Object.assign({ dataLength: total, next: fetchSubscribers, hasMoreNext: next !== null, loaderNext: _jsx(CentralProgress, { size: 30 }), height: 400, endMessage: _jsx(Typography, Object.assign({ variant: "body2", align: "center", fontWeight: "bold" }, { children: _jsx(FormattedMessage, { id: "ui.incubatorDetail.subscribersSection.noMoreSubscribers", defaultMessage: "ui.incubatorDetail.subscribersSection.noMoreSubscribers" }) })) }, { children: _jsx(List, { children: subscribers.map((s, index) => (_jsx(ListItem, { children: _jsx(User, { elevation: 0, user: s }, index) }, (s.id, index)))) }) }))) }))), _jsx(Widget, Object.assign({ elevation: 1, className: classes.shareCard }, { children: _jsxs(CardContent, { children: [_jsx(Typography, Object.assign({ variant: 'h6' }, { children: _jsx(FormattedMessage, { id: "ui.incubatorDetail.shareSection.title", defaultMessage: "ui.incubatorDetail.shareSection.title" }) })), _jsx(Typography, Object.assign({ variant: 'subtitle1' }, { children: _jsx(FormattedMessage, { id: "ui.incubatorDetail.shareSection.share", defaultMessage: "ui.incubatorDetail.shareSection.share" }) })), _jsxs(FormGroup, Object.assign({ className: classes.copyUrlForm }, { children: [_jsx(TextField, { className: classes.copyText, variant: "outlined", value: portal }), _jsx(Button, Object.assign({ className: classes.copyButton, variant: "contained", onClick: copy }, { children: _jsx(FormattedMessage, { id: "ui.incubatorDetail.shareSection.button.copy", defaultMessage: "ui.incubatorDetail.shareSection.button.copy" }) }))] })), alert && (_jsx(Alert, Object.assign({ onClose: () => setAlert(false) }, { children: _jsx(FormattedMessage, { id: "ui.incubatorDetail.shareSection.copied", defaultMessage: "ui.incubatorDetail.shareSection.copied" }) }))), isSocialShareEnabled && (_jsx(Typography, Object.assign({ variant: 'subtitle2' }, { children: _jsx(FormattedMessage, { id: "ui.incubatorDetail.shareSection.invite", defaultMessage: "ui.incubatorDetail.shareSection.invite" }) }))), _jsxs(Box, Object.assign({ className: classes.shareSection }, { children: [facebookShareEnabled && (_jsx(Icon, Object.assign({ classes: { root: classes.shareMenuIcon }, fontSize: "small", onClick: () => window.open(FACEBOOK_SHARE + portal, 'facebook-share-dialog', 'width=626,height=436') }, { children: "facebook" }))), xShareEnabled && (_jsx(Icon, Object.assign({ classes: { root: classes.shareMenuIcon }, fontSize: "small", onClick: () => window.open(X_SHARE + portal, 'x-share-dialog', 'width=626,height=436') }, { children: "x" }))), linkedinShareEnabled && (_jsx(Icon, Object.assign({ classes: { root: classes.shareMenuIcon }, fontSize: "small", onClick: () => window.open(LINKEDIN_SHARE + portal, 'linkedin-share-dialog', 'width=626,height=436') }, { children: "linkedin" })))] }))] }) }))] }) })), openAlert && _jsx(UserDeletedSnackBar, { open: openAlert, handleClose: () => setOpenAlert(false) })] }));
}