feat(ui): change share modal in player (#1958)
* feat(ui): change share modal in player * fix(ui): rm console
This commit is contained in:
parent
0f9eca733a
commit
11a1cf709f
6 changed files with 303 additions and 220 deletions
|
|
@ -114,6 +114,7 @@ function SubHeader(props: any) {
|
|||
},
|
||||
{
|
||||
key: 4,
|
||||
autoclose: true,
|
||||
component: (
|
||||
<SharePopup
|
||||
entity="sessions"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
|
|||
|
||||
interface MenuItem {
|
||||
key: number;
|
||||
autoclose?: boolean;
|
||||
component?: React.ReactElement;
|
||||
}
|
||||
|
||||
|
|
@ -53,6 +54,7 @@ export default class ItemMenu extends React.PureComponent<Props> {
|
|||
item.component ? (
|
||||
<div
|
||||
key={item.key}
|
||||
onClick={item.autoclose ? this.closeMenu : undefined}
|
||||
role="menuitem"
|
||||
className="hover:bg-gray-light-shade cursor-pointer flex items-center w-full"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import { DateTime } from 'luxon';
|
|||
function SessionCopyLink({ startedAt }: any) {
|
||||
const [copied, setCopied] = React.useState(false);
|
||||
const { store } = React.useContext(PlayerContext);
|
||||
const time = store.get().time;
|
||||
|
||||
const time = store?.get().time;
|
||||
|
||||
const copyHandler = () => {
|
||||
setCopied(true);
|
||||
|
|
|
|||
|
|
@ -1,211 +0,0 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Form, Button, Popover, Loader } from 'UI';
|
||||
import styles from './sharePopup.module.css';
|
||||
import IntegrateSlackButton from '../IntegrateSlackButton/IntegrateSlackButton';
|
||||
import SessionCopyLink from './SessionCopyLink';
|
||||
import Select from 'Shared/Select';
|
||||
import cn from 'classnames';
|
||||
import { fetchList as fetchSlack, sendSlackMsg } from 'Duck/integrations/slack';
|
||||
import { fetchList as fetchTeams, sendMsTeamsMsg } from 'Duck/integrations/teams';
|
||||
|
||||
@connect(
|
||||
(state) => ({
|
||||
sessionId: state.getIn(['sessions', 'current']).sessionId,
|
||||
channels: state.getIn(['slack', 'list']),
|
||||
slackLoaded: state.getIn(['slack', 'loaded']),
|
||||
msTeamsChannels: state.getIn(['teams', 'list']),
|
||||
msTeamsLoaded: state.getIn(['teams', 'loaded']),
|
||||
tenantId: state.getIn(['user', 'account', 'tenantId']),
|
||||
}),
|
||||
{ fetchSlack, fetchTeams, sendSlackMsg, sendMsTeamsMsg }
|
||||
)
|
||||
export default class SharePopup extends React.PureComponent {
|
||||
state = {
|
||||
comment: '',
|
||||
isOpen: false,
|
||||
channelId: this.props.channels.getIn([0, 'webhookId']),
|
||||
teamsChannel: this.props.msTeamsChannels.getIn([0, 'webhookId']),
|
||||
loadingSlack: false,
|
||||
loadingTeams: false,
|
||||
};
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.state.isOpen) {
|
||||
if (this.props.channels.size === 0 && !this.props.slackLoaded) {
|
||||
this.props.fetchSlack();
|
||||
}
|
||||
if (this.props.msTeamsChannels.size === 0 && !this.props.msTeamsLoaded) {
|
||||
this.props.fetchTeams();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
editMessage = (e) => this.setState({ comment: e.target.value });
|
||||
shareToSlack = () => {
|
||||
this.setState({ loadingSlack: true }, () => {
|
||||
this.props
|
||||
.sendSlackMsg({
|
||||
integrationId: this.state.channelId,
|
||||
entity: 'sessions',
|
||||
entityId: this.props.sessionId,
|
||||
data: { comment: this.state.comment },
|
||||
})
|
||||
.then(() => this.handleSuccess('Slack'));
|
||||
});
|
||||
};
|
||||
|
||||
shareToMSTeams = () => {
|
||||
this.setState({ loadingTeams: true }, () => {
|
||||
this.props
|
||||
.sendMsTeamsMsg({
|
||||
integrationId: this.state.teamsChannel,
|
||||
entity: 'sessions',
|
||||
entityId: this.props.sessionId,
|
||||
data: { comment: this.state.comment },
|
||||
})
|
||||
.then(() => this.handleSuccess('MS Teams'));
|
||||
});
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
setTimeout(function () {
|
||||
document.getElementById('message').focus();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ comment: '' });
|
||||
};
|
||||
|
||||
handleSuccess = (endpoint) => {
|
||||
const obj = endpoint === 'Slack' ? { loadingSlack: false } : { loadingTeams: false };
|
||||
this.setState(obj);
|
||||
toast.success(`Sent to ${endpoint}.`);
|
||||
};
|
||||
|
||||
changeSlackChannel = ({ value }) => this.setState({ channelId: value.value });
|
||||
|
||||
changeTeamsChannel = ({ value }) => this.setState({ teamsChannel: value.value });
|
||||
|
||||
onClickHandler = () => {
|
||||
this.setState({ isOpen: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { trigger, channels, msTeamsChannels, showCopyLink = false } = this.props;
|
||||
const { comment, channelId, teamsChannel, loadingSlack, loadingTeams } = this.state;
|
||||
|
||||
const slackOptions = channels
|
||||
.map(({ webhookId, name }) => ({ value: webhookId, label: name }))
|
||||
.toJS();
|
||||
|
||||
const msTeamsOptions = msTeamsChannels
|
||||
.map(({ webhookId, name }) => ({ value: webhookId, label: name }))
|
||||
.toJS();
|
||||
|
||||
return (
|
||||
<Popover
|
||||
onOpen={() => this.setState({ isOpen: true })}
|
||||
onClose={() => this.setState({ isOpen: false, comment: '' })}
|
||||
render={() => (
|
||||
<div className={styles.wrapper}>
|
||||
{this.state.loadingTeams || this.state.loadingSlack ? (
|
||||
<Loader loading />
|
||||
) : (
|
||||
<>
|
||||
<div className="text-xl mr-4 font-semibold mb-4">
|
||||
Share
|
||||
</div>
|
||||
{slackOptions.length > 0 || msTeamsOptions.length > 0 ? (
|
||||
<div>
|
||||
<div className={styles.body}>
|
||||
<textarea
|
||||
name="message"
|
||||
id="message"
|
||||
cols="30"
|
||||
rows="4"
|
||||
resize="none"
|
||||
onChange={this.editMessage}
|
||||
value={comment}
|
||||
placeholder="Add Message (Optional)"
|
||||
className="p-4 text-figmaColors-text-primary text-base"
|
||||
/>
|
||||
|
||||
{slackOptions.length > 0 && (
|
||||
<Form.Field className="mb-15-imp">
|
||||
<label>Share to slack</label>
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<Select
|
||||
options={slackOptions}
|
||||
defaultValue={channelId}
|
||||
onChange={this.changeSlackChannel}
|
||||
className="col-span-4"
|
||||
/>
|
||||
{this.state.channelId && (
|
||||
<Button
|
||||
onClick={this.shareToSlack}
|
||||
icon="integrations/slack-bw"
|
||||
variant="outline"
|
||||
className="col-span-2"
|
||||
>
|
||||
{loadingSlack ? 'Sending...' : 'Send'}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Form.Field>
|
||||
)}
|
||||
{msTeamsOptions.length > 0 && (
|
||||
<Form.Field className="mb-15-imp">
|
||||
<label>Share to MS Teams</label>
|
||||
<div className="grid grid-cols-6 gap-4">
|
||||
<Select
|
||||
options={msTeamsOptions}
|
||||
defaultValue={teamsChannel}
|
||||
onChange={this.changeTeamsChannel}
|
||||
className="col-span-4"
|
||||
/>
|
||||
{this.state.teamsChannel && (
|
||||
<Button
|
||||
onClick={this.shareToMSTeams}
|
||||
icon="integrations/teams-white"
|
||||
variant="outline"
|
||||
className="col-span-2"
|
||||
>
|
||||
{loadingTeams ? 'Sending...' : 'Send'}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Form.Field>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.footer}>
|
||||
<SessionCopyLink />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className={styles.body}>
|
||||
<IntegrateSlackButton />
|
||||
</div>
|
||||
{showCopyLink && (
|
||||
<>
|
||||
<div className="border-t -mx-2" />
|
||||
<div>
|
||||
<SessionCopyLink />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
{trigger}
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
}
|
||||
289
frontend/app/components/shared/SharePopup/SharePopup.tsx
Normal file
289
frontend/app/components/shared/SharePopup/SharePopup.tsx
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
import { useModal } from 'Components/Modal';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Icon, Loader } from 'UI';
|
||||
import styles from './sharePopup.module.css';
|
||||
import IntegrateSlackButton from '../IntegrateSlackButton/IntegrateSlackButton';
|
||||
import SessionCopyLink from './SessionCopyLink';
|
||||
import Select from 'Shared/Select';
|
||||
import { fetchList as fetchSlack, sendSlackMsg } from 'Duck/integrations/slack';
|
||||
import { fetchList as fetchTeams, sendMsTeamsMsg } from 'Duck/integrations/teams';
|
||||
import { Button, Segmented } from 'antd';
|
||||
|
||||
interface Msg {
|
||||
integrationId: string;
|
||||
entity: 'sessions';
|
||||
entityId: string;
|
||||
data: { comment: string };
|
||||
}
|
||||
|
||||
const SharePopup = ({
|
||||
trigger,
|
||||
showCopyLink = false,
|
||||
}: {
|
||||
trigger: string;
|
||||
showCopyLink?: boolean;
|
||||
}) => {
|
||||
const { showModal, hideModal } = useModal();
|
||||
|
||||
const openModal = () => {
|
||||
showModal(
|
||||
<ShareModal
|
||||
hideModal={hideModal}
|
||||
showCopyLink={showCopyLink}
|
||||
/>,
|
||||
{ right: true, width: 300 }
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'w-full h-full'} onClick={openModal}>
|
||||
{trigger}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
interface Props {
|
||||
sessionId: string;
|
||||
channels: { webhookId: string; name: string }[];
|
||||
slackLoaded: boolean;
|
||||
msTeamsChannels: { webhookId: string; name: string }[];
|
||||
msTeamsLoaded: boolean;
|
||||
tenantId: string;
|
||||
fetchSlack: () => void;
|
||||
fetchTeams: () => void;
|
||||
sendSlackMsg: (msg: Msg) => any;
|
||||
sendMsTeamsMsg: (msg: Msg) => any;
|
||||
showCopyLink?: boolean;
|
||||
hideModal: () => void;
|
||||
}
|
||||
|
||||
function ShareModalComp({
|
||||
sessionId,
|
||||
sendSlackMsg,
|
||||
sendMsTeamsMsg,
|
||||
showCopyLink,
|
||||
channels,
|
||||
slackLoaded,
|
||||
msTeamsChannels,
|
||||
msTeamsLoaded,
|
||||
fetchSlack,
|
||||
fetchTeams,
|
||||
hideModal,
|
||||
}: Props) {
|
||||
const [shareTo, setShareTo] = useState('slack');
|
||||
const [comment, setComment] = useState('');
|
||||
// @ts-ignore
|
||||
const [channelId, setChannelId] = useState(channels.getIn([0, 'webhookId']));
|
||||
// @ts-ignore
|
||||
const [teamsChannel, setTeamsChannel] = useState(msTeamsChannels.getIn([0, 'webhookId']));
|
||||
const [loadingSlack, setLoadingSlack] = useState(false);
|
||||
const [loadingTeams, setLoadingTeams] = useState(false);
|
||||
|
||||
const isLoading = loadingSlack || loadingTeams;
|
||||
|
||||
useEffect(() => {
|
||||
// @ts-ignore
|
||||
if (channels.size === 0 && !slackLoaded) {
|
||||
fetchSlack();
|
||||
}
|
||||
// @ts-ignore
|
||||
if (msTeamsChannels.size === 0 && !msTeamsLoaded) {
|
||||
fetchTeams();
|
||||
}
|
||||
}, [channels, slackLoaded, msTeamsChannels, msTeamsLoaded, fetchSlack, fetchTeams]);
|
||||
|
||||
const editMessage = (e: React.ChangeEvent<HTMLTextAreaElement>) => setComment(e.target.value);
|
||||
const shareToSlack = () => {
|
||||
setLoadingSlack(true);
|
||||
sendSlackMsg({
|
||||
integrationId: channelId,
|
||||
entity: 'sessions',
|
||||
entityId: sessionId,
|
||||
data: { comment },
|
||||
}).then(() => handleSuccess('Slack'));
|
||||
};
|
||||
|
||||
const shareToMSTeams = () => {
|
||||
setLoadingTeams(true);
|
||||
sendMsTeamsMsg({
|
||||
integrationId: teamsChannel,
|
||||
entity: 'sessions',
|
||||
entityId: sessionId,
|
||||
data: { comment },
|
||||
}).then(() => handleSuccess('MS Teams'));
|
||||
};
|
||||
|
||||
const handleSuccess = (endpoint: string) => {
|
||||
if (endpoint === 'Slack') {
|
||||
setLoadingSlack(false);
|
||||
} else {
|
||||
setLoadingTeams(false);
|
||||
}
|
||||
// @ts-ignore
|
||||
toast.success(`Sent to ${endpoint}.`);
|
||||
};
|
||||
|
||||
const changeSlackChannel = (value: any) => setChannelId(value.value);
|
||||
const changeTeamsChannel = (value: any) => setTeamsChannel(value.value);
|
||||
|
||||
const slackOptions = channels
|
||||
.map(({ webhookId, name }) => ({
|
||||
value: webhookId,
|
||||
label: name,
|
||||
}))
|
||||
// @ts-ignore
|
||||
.toJS();
|
||||
|
||||
const msTeamsOptions = msTeamsChannels
|
||||
.map(({ webhookId, name }) => ({
|
||||
value: webhookId,
|
||||
label: name,
|
||||
}))
|
||||
// @ts-ignore
|
||||
.toJS();
|
||||
|
||||
const sendMsg = () => {
|
||||
if (shareTo === 'slack') {
|
||||
shareToSlack();
|
||||
} else {
|
||||
shareToMSTeams();
|
||||
}
|
||||
hideModal();
|
||||
}
|
||||
const hasBoth = slackOptions.length > 0 && msTeamsOptions.length > 0;
|
||||
const hasNothing = slackOptions.length === 0 && msTeamsOptions.length === 0;
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
{isLoading ? (
|
||||
<Loader loading />
|
||||
) : (
|
||||
<>
|
||||
<div className="text-xl mr-4 font-semibold mb-4 flex items-center gap-2">
|
||||
<Icon name={'share-alt'} size={16} />
|
||||
<div>Share Session</div>
|
||||
</div>
|
||||
{!hasNothing ? (
|
||||
<div>
|
||||
<div className={'flex flex-col gap-4'}>
|
||||
<div>
|
||||
<div className={'font-semibold flex items-center'}>
|
||||
Share via
|
||||
</div>
|
||||
{hasBoth ? (
|
||||
<Segmented
|
||||
options={[
|
||||
{
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
<Icon name="integrations/slack-bw" size={16} />
|
||||
<div>Slack</div>
|
||||
</div>,
|
||||
value: 'slack',
|
||||
},
|
||||
{
|
||||
label: <div className={'flex items-center gap-2'}>
|
||||
<Icon name="integrations/teams-white" size={16} />
|
||||
<div>MS Teams</div>
|
||||
</div>,
|
||||
value: 'teams',
|
||||
},
|
||||
]}
|
||||
onChange={(value) => setShareTo(value as 'slack' | 'teams')}
|
||||
/>
|
||||
) : (
|
||||
<div>
|
||||
<Icon
|
||||
name={
|
||||
slackOptions.length > 0
|
||||
? 'integrations/slack-bw'
|
||||
: 'integrations/teams-white'
|
||||
}
|
||||
/>
|
||||
<div>{slackOptions.length > 0 ? 'Slack' : 'MS Teams'}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className={'font-semibold'}>Select a channel or individual</div>
|
||||
{shareTo === 'slack' ? (
|
||||
<Select
|
||||
options={slackOptions}
|
||||
defaultValue={channelId}
|
||||
onChange={changeSlackChannel}
|
||||
className="col-span-4"
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
options={msTeamsOptions}
|
||||
defaultValue={teamsChannel}
|
||||
onChange={changeTeamsChannel}
|
||||
className="col-span-4"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className={'font-semibold'}>Message</div>
|
||||
<textarea
|
||||
name="message"
|
||||
id="message"
|
||||
cols={30}
|
||||
rows={4}
|
||||
onChange={editMessage}
|
||||
value={comment}
|
||||
placeholder="Add Message (Optional)"
|
||||
className="p-4 text-figmaColors-text-primary text-base bg-white border rounded border-gray-light"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={'mt-4'}>
|
||||
<SessionCopyLink />
|
||||
<div className={'flex items-center gap-2 pt-8 mt-4 border-t'}>
|
||||
<Button type={'primary'} onClick={sendMsg}>Send</Button>
|
||||
<Button type={'primary'} ghost onClick={hideModal}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className={styles.body}>
|
||||
<IntegrateSlackButton />
|
||||
</div>
|
||||
{showCopyLink && (
|
||||
<>
|
||||
<div className="border-t -mx-2" />
|
||||
<div>
|
||||
<SessionCopyLink />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: Record<string, any>) => ({
|
||||
sessionId: state.getIn(['sessions', 'current']).sessionId,
|
||||
channels: state.getIn(['slack', 'list']),
|
||||
slackLoaded: state.getIn(['slack', 'loaded']),
|
||||
msTeamsChannels: state.getIn(['teams', 'list']),
|
||||
msTeamsLoaded: state.getIn(['teams', 'loaded']),
|
||||
tenantId: state.getIn(['user', 'account', 'tenantId']),
|
||||
});
|
||||
|
||||
const ShareModal = connect(mapStateToProps, {
|
||||
fetchSlack,
|
||||
fetchTeams,
|
||||
sendSlackMsg,
|
||||
sendMsTeamsMsg,
|
||||
})(ShareModalComp);
|
||||
|
||||
export default SharePopup;
|
||||
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
.wrapper {
|
||||
background-color: white;
|
||||
width: 390px;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 10px 8px;
|
||||
color: $gray-darkest !important;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue