1 import { useEffect, useState } from 'react';
3 import { c } from 'ttag';
5 import { useUser } from '@proton/account/user/hooks';
6 import { useOAuthToken } from '@proton/activation';
7 import { createToken } from '@proton/activation/src/api';
8 import useOAuthPopup from '@proton/activation/src/hooks/useOAuthPopup';
9 import type { OAuthProps } from '@proton/activation/src/interface';
10 import { EASY_SWITCH_SOURCES, ImportType, OAUTH_PROVIDER } from '@proton/activation/src/interface';
11 import { Button, CircleLoader } from '@proton/atoms';
12 import { Icon, IconRow, useApi } from '@proton/components';
13 import { useLoading } from '@proton/hooks';
14 import { createZoomMeeting } from '@proton/shared/lib/api/calendars';
15 import type { EventModel, VideoConferenceMeetingCreation } from '@proton/shared/lib/interfaces/calendar';
16 import zoomLogo from '@proton/styles/assets/img/video-conferencing/zoom.svg';
17 import clsx from '@proton/utils/clsx';
19 import { VideoConferencingWidget } from '../videoConferencing/VideoConferencingWidget';
20 import { VIDEO_CONF_SERVICES } from '../videoConferencing/constants';
22 type ZoomIntegrationState = 'loadingConfig' | 'disconnected' | 'connected' | 'loading' | 'meeting-present';
24 const getIcon = (state: ZoomIntegrationState) => {
28 return <img src={zoomLogo} className="h-6 w-6 hidden" alt="" />;
29 case 'meeting-present':
30 return <img src={zoomLogo} className="h-6 w-6" alt="" />;
33 return <CircleLoader className="color-primary h-4 w-4" />;
39 setModel: (value: EventModel) => void;
42 export const ZoomRow = ({ model, setModel }: Props) => {
43 const [user] = useUser();
44 const [oAuthToken, oauthTokenLoading] = useOAuthToken();
45 const isUserConnectedToZoom = oAuthToken?.some(({ Provider }) => Provider === OAUTH_PROVIDER.ZOOM);
47 const [processState, setProcessState] = useState<ZoomIntegrationState>('loadingConfig');
50 const [, withLoading] = useLoading();
51 const { triggerOAuthPopup, loadingConfig } = useOAuthPopup({
52 errorMessage: c('Error').t`Failed to load oauth modal.`,
56 if (loadingConfig || oauthTokenLoading) {
57 setProcessState('loadingConfig');
59 setProcessState(isUserConnectedToZoom ? 'connected' : 'disconnected');
61 }, [loadingConfig, oauthTokenLoading]);
64 if (model.conferenceUrl) {
65 setProcessState('meeting-present');
69 const createVideoConferenceMeeting = async () => {
70 setProcessState('loading');
71 const data = await withLoading(api<VideoConferenceMeetingCreation>(createZoomMeeting()));
75 conferenceId: data?.VideoConference?.ID,
76 conferenceUrl: data?.VideoConference?.URL,
77 conferencePasscode: data?.VideoConference?.Password,
78 conferenceCreator: user.ID,
80 setProcessState('meeting-present');
83 const handleClick = async () => {
85 // TODO display upsell for Zoom, will be done in a separate MR
86 alert('Display upsell for zoom');
90 if (processState === 'disconnected') {
92 provider: OAUTH_PROVIDER.ZOOM,
94 callback: async (oAuthProps: OAuthProps) => {
95 const { Code, Provider, RedirectUri } = oAuthProps;
102 Source: EASY_SWITCH_SOURCES.CALENDAR_WEB_CREATE_EVENT,
103 Products: [ImportType.CALENDAR],
106 await createVideoConferenceMeeting();
110 await createVideoConferenceMeeting();
114 if (processState === 'meeting-present') {
116 <VideoConferencingWidget
119 service: VIDEO_CONF_SERVICES.ZOOM,
120 meetingId: model.conferenceId,
121 meetingUrl: model.conferenceUrl,
122 password: model.conferencePasscode,
129 <IconRow icon={getIcon(processState)} labelClassName={clsx(processState === 'loading' && 'my-auto p-0')}>
130 {(processState === 'connected' || processState === 'disconnected') && (
131 <div className="flex items-center gap-1">
133 onClick={handleClick}
134 disabled={loadingConfig || oauthTokenLoading}
135 loading={loadingConfig || oauthTokenLoading}
141 {c('Zoom integration').t`Add Zoom meeting`}
143 {user.isFree && <Icon name="upgrade" className="color-primary" />}
147 {(processState === 'loading' || processState === 'loadingConfig') && (
148 <Button disabled shape="ghost" className="p-0" color="norm" size="small">
150 ? c('Zoom integration').t`Loading Zoom configuration`
151 : c('Zoom integration').t`Adding conferencing details`}