jiti-meet/react/features/calendar-sync/actions.web.ts

294 lines
8.5 KiB
TypeScript

// @ts-expect-error
import { generateRoomWithoutSeparator } from '@jitsi/js-utils/random';
import { createCalendarConnectedEvent } from '../analytics/AnalyticsEvents';
import { sendAnalytics } from '../analytics/functions';
import { IStore } from '../app/types';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import { loadGoogleAPI } from '../google-api';
import {
CLEAR_CALENDAR_INTEGRATION,
SET_CALENDAR_AUTH_STATE,
SET_CALENDAR_ERROR,
SET_CALENDAR_INTEGRATION,
SET_CALENDAR_PROFILE_EMAIL,
SET_LOADING_CALENDAR_EVENTS
} from './actionTypes';
import { refreshCalendar, setCalendarEvents } from './actions.web';
import { _getCalendarIntegration, isCalendarEnabled } from './functions.web';
import logger from './logger';
export * from './actions.any';
/**
* Sets the initial state of calendar integration by loading third party APIs
* and filling out any data that needs to be fetched.
*
* @returns {Function}
*/
export function bootstrapCalendarIntegration() {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
if (!isCalendarEnabled(state)) {
return Promise.reject();
}
const {
googleApiApplicationClientID
} = state['features/base/config'];
const {
integrationReady,
integrationType
} = state['features/calendar-sync'];
return Promise.resolve()
.then(() => {
if (googleApiApplicationClientID) {
return dispatch(loadGoogleAPI());
}
})
.then(() => {
if (!integrationType || integrationReady) {
return;
}
const integrationToLoad
= _getCalendarIntegration(integrationType);
if (!integrationToLoad) {
dispatch(clearCalendarIntegration());
return;
}
return dispatch(integrationToLoad._isSignedIn())
.then((signedIn: boolean) => {
if (signedIn) {
dispatch(setIntegrationReady(integrationType));
dispatch(updateProfile(integrationType));
} else {
dispatch(clearCalendarIntegration());
}
});
});
};
}
/**
* Resets the state of calendar integration so stored events and selected
* calendar type are cleared.
*
* @returns {{
* type: CLEAR_CALENDAR_INTEGRATION
* }}
*/
export function clearCalendarIntegration() {
return {
type: CLEAR_CALENDAR_INTEGRATION
};
}
/**
* Asks confirmation from the user to add a Jitsi link to the calendar event.
*
* NOTE: Currently there is no confirmation prompted on web, so this is just
* a relaying method to avoid flow problems.
*
* @param {string} eventId - The event id.
* @param {string} calendarId - The calendar id.
* @returns {Function}
*/
export function openUpdateCalendarEventDialog(
eventId: string, calendarId: string) {
return updateCalendarEvent(eventId, calendarId);
}
/**
* Sends an action to update the current calendar api auth state in redux.
* This is used only for microsoft implementation to store it auth state.
*
* @param {number} newState - The new state.
* @returns {{
* type: SET_CALENDAR_AUTH_STATE,
* msAuthState: Object
* }}
*/
export function setCalendarAPIAuthState(newState?: Object) {
return {
type: SET_CALENDAR_AUTH_STATE,
msAuthState: newState
};
}
/**
* Sends an action to update the calendar error state in redux.
*
* @param {Object} error - An object with error details.
* @returns {{
* type: SET_CALENDAR_ERROR,
* error: Object
* }}
*/
export function setCalendarError(error?: Object) {
return {
type: SET_CALENDAR_ERROR,
error
};
}
/**
* Sends an action to update the current calendar profile email state in redux.
*
* @param {number} newEmail - The new email.
* @returns {{
* type: SET_CALENDAR_PROFILE_EMAIL,
* email: string
* }}
*/
export function setCalendarProfileEmail(newEmail?: string) {
return {
type: SET_CALENDAR_PROFILE_EMAIL,
email: newEmail
};
}
/**
* Sends an to denote a request in is flight to get calendar events.
*
* @param {boolean} isLoadingEvents - Whether or not calendar events are being
* fetched.
* @returns {{
* type: SET_LOADING_CALENDAR_EVENTS,
* isLoadingEvents: boolean
* }}
*/
export function setLoadingCalendarEvents(isLoadingEvents: boolean) {
return {
type: SET_LOADING_CALENDAR_EVENTS,
isLoadingEvents
};
}
/**
* Sets the calendar integration type to be used by web and signals that the
* integration is ready to be used.
*
* @param {string|undefined} integrationType - The calendar type.
* @returns {{
* type: SET_CALENDAR_INTEGRATION,
* integrationReady: boolean,
* integrationType: string
* }}
*/
export function setIntegrationReady(integrationType: string) {
return {
type: SET_CALENDAR_INTEGRATION,
integrationReady: true,
integrationType
};
}
/**
* Signals signing in to the specified calendar integration.
*
* @param {string} calendarType - The calendar integration which should be
* signed into.
* @returns {Function}
*/
export function signIn(calendarType: string) {
return (dispatch: IStore['dispatch']) => {
const integration = _getCalendarIntegration(calendarType);
if (!integration) {
return Promise.reject('No supported integration found');
}
return dispatch(integration.load())
.then(() => dispatch(integration.signIn()))
.then(() => dispatch(setIntegrationReady(calendarType)))
.then(() => dispatch(updateProfile(calendarType)))
.then(() => dispatch(refreshCalendar()))
.then(() => sendAnalytics(createCalendarConnectedEvent()))
.catch((error: any) => {
logger.error(
'Error occurred while signing into calendar integration',
error);
return Promise.reject(error);
});
};
}
/**
* Updates calendar event by generating new invite URL and editing the event
* adding some descriptive text and location.
*
* @param {string} id - The event id.
* @param {string} calendarId - The id of the calendar to use.
* @returns {Function}
*/
export function updateCalendarEvent(id: string, calendarId: string) {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const { integrationType = '' } = getState()['features/calendar-sync'];
const integration = _getCalendarIntegration(integrationType);
if (!integration) {
return Promise.reject('No integration found');
}
const { locationURL } = getState()['features/base/connection'];
const newRoomName = generateRoomWithoutSeparator();
let href = locationURL?.href ?? '';
href.endsWith('/') || (href += '/');
const roomURL = `${href}${newRoomName}`;
return dispatch(integration.updateCalendarEvent(
id, calendarId, roomURL))
.then(() => {
// make a copy of the array
const events
= getState()['features/calendar-sync'].events.slice(0);
const eventIx = events.findIndex(
e => e.id === id && e.calendarId === calendarId);
// clone the event we will modify
const newEvent = Object.assign({}, events[eventIx]);
newEvent.url = roomURL;
events[eventIx] = newEvent;
return dispatch(setCalendarEvents(events));
});
};
}
/**
* Signals to get current profile data linked to the current calendar
* integration that is in use.
*
* @param {string} calendarType - The calendar integration to which the profile
* should be updated.
* @returns {Function}
*/
export function updateProfile(calendarType: string) {
return (dispatch: IStore['dispatch']) => {
const integration = _getCalendarIntegration(calendarType);
if (!integration) {
return Promise.reject('No integration found');
}
return dispatch(integration.getCurrentEmail())
.then((email: string) => {
dispatch(setCalendarProfileEmail(email));
});
};
}