Daily video calls (#542)
* ⬆️ Bump tailwindcss from 2.2.14 to 2.2.15 Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 2.2.14 to 2.2.15. - [Release notes](https://github.com/tailwindlabs/tailwindcss/releases) - [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md) - [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v2.2.14...v2.2.15) --- updated-dependencies: - dependency-name: tailwindcss dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> * updating cal will provide a zoom meeting url * updating cal will provide a zoom meeting url * modifying how daily emails send * modifying how daily emails send * daily table * migration updates * daily table * rebasing updates * updating Daily references to a new table * updating internal notes * merge updates, adding Daily references to book/events.ts * updated video email templates to remove Daily specific references * updating the events.ts and refactoring in the event manager * removing the package-lock * changing calendso video powered by Daily.co to cal video powered by Daily.co * updating some of the internal Daily notes * added a modal for when the call/ link is invalid * removing handle errors raw from the Daily video client * prettier formatting fixes * Added the Daily location to calendar events and updated Cal video references to Daily.co video * updating references to create in event manager to check for Daily video * fixing spacing on the cancel booking modal and adding Daily references in the event manager * formatting fixes * updating the readme file * adding a daily interface in the event manager * adding daily to the location labels * added a note to cal event parser * resolving yarn merge conflicts * updating dailyReturn to DailyReturnType * removing prettier auto and refactoring integrations: daily in the event manager * removing changes to estlintrc.json * updating read me formatting * indent space for Daily ReadMe section * resolving the merge conflicts in the yarn file Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Lola-Ojabowale <lola.ojabowale@gmail.com>
This commit is contained in:
parent
58de920951
commit
adee3fd211
20 changed files with 761 additions and 41 deletions
|
@ -31,6 +31,9 @@ MS_GRAPH_CLIENT_SECRET=
|
||||||
ZOOM_CLIENT_ID=
|
ZOOM_CLIENT_ID=
|
||||||
ZOOM_CLIENT_SECRET=
|
ZOOM_CLIENT_SECRET=
|
||||||
|
|
||||||
|
#Used for the Daily integration
|
||||||
|
DAILY_API_KEY=
|
||||||
|
|
||||||
# E-mail settings
|
# E-mail settings
|
||||||
|
|
||||||
# Cal uses nodemailer (@see https://nodemailer.com/about/) to provide email sending. As such we are trying to
|
# Cal uses nodemailer (@see https://nodemailer.com/about/) to provide email sending. As such we are trying to
|
||||||
|
|
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "pwa-chrome",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch Chrome against localhost",
|
||||||
|
"url": "http://localhost:8080",
|
||||||
|
"webRoot": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -283,6 +283,13 @@ Contributions are what make the open source community such an amazing place to b
|
||||||
12. Click "Done".
|
12. Click "Done".
|
||||||
13. You're good to go. Now you can easily add your Zoom integration in the Cal.com settings.
|
13. You're good to go. Now you can easily add your Zoom integration in the Cal.com settings.
|
||||||
|
|
||||||
|
## Obtaining Daily API Credentials
|
||||||
|
|
||||||
|
1. Open [Daily](https://www.daily.co/) and sign into your account.
|
||||||
|
2. From within your dashboard, go to the [developers](https://dashboard.daily.co/developers) tab.
|
||||||
|
3. Copy your API key.
|
||||||
|
4. Now paste the API key to your .env file into the `DAILY_API_KEY` field in your .env file.
|
||||||
|
|
||||||
<!-- LICENSE -->
|
<!-- LICENSE -->
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
|
@ -71,6 +71,7 @@ const BookingPage = (props: BookingPageProps) => {
|
||||||
[LocationType.Phone]: "Phone call",
|
[LocationType.Phone]: "Phone call",
|
||||||
[LocationType.GoogleMeet]: "Google Meet",
|
[LocationType.GoogleMeet]: "Google Meet",
|
||||||
[LocationType.Zoom]: "Zoom Video",
|
[LocationType.Zoom]: "Zoom Video",
|
||||||
|
[LocationType.Daily]: "Daily.co Video",
|
||||||
};
|
};
|
||||||
|
|
||||||
const _bookingHandler = (event) => {
|
const _bookingHandler = (event) => {
|
||||||
|
|
|
@ -80,13 +80,17 @@ export default class CalEventParser {
|
||||||
/**
|
/**
|
||||||
* Conditionally returns the event's location. When VideoCallData is set,
|
* Conditionally returns the event's location. When VideoCallData is set,
|
||||||
* it returns the meeting url. Otherwise, the regular location is returned.
|
* it returns the meeting url. Otherwise, the regular location is returned.
|
||||||
*
|
* For Daily video calls returns the direct link
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected getLocation(): string | undefined {
|
protected getLocation(): string | undefined {
|
||||||
|
const isDaily = this.calEvent.location === "integrations:daily";
|
||||||
if (this.optionalVideoCallData) {
|
if (this.optionalVideoCallData) {
|
||||||
return this.optionalVideoCallData.url;
|
return this.optionalVideoCallData.url;
|
||||||
}
|
}
|
||||||
|
if (isDaily) {
|
||||||
|
return process.env.BASE_URL + "/call/" + this.getUid();
|
||||||
|
}
|
||||||
return this.calEvent.location;
|
return this.calEvent.location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
239
lib/dailyVideoClient.ts
Normal file
239
lib/dailyVideoClient.ts
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
import { Credential } from "@prisma/client";
|
||||||
|
import short from "short-uuid";
|
||||||
|
import { v5 as uuidv5 } from "uuid";
|
||||||
|
|
||||||
|
import CalEventParser from "@lib/CalEventParser";
|
||||||
|
import { AdditionInformation, EntryPoint } from "@lib/emails/EventMail";
|
||||||
|
import { getIntegrationName } from "@lib/emails/helpers";
|
||||||
|
import { EventResult } from "@lib/events/EventManager";
|
||||||
|
import logger from "@lib/logger";
|
||||||
|
|
||||||
|
import { CalendarEvent } from "./calendarClient";
|
||||||
|
import EventAttendeeRescheduledMail from "./emails/EventAttendeeRescheduledMail";
|
||||||
|
import EventOrganizerRescheduledMail from "./emails/EventOrganizerRescheduledMail";
|
||||||
|
import VideoEventAttendeeMail from "./emails/VideoEventAttendeeMail";
|
||||||
|
import VideoEventOrganizerMail from "./emails/VideoEventOrganizerMail";
|
||||||
|
|
||||||
|
const log = logger.getChildLogger({ prefix: ["[lib] dailyVideoClient"] });
|
||||||
|
|
||||||
|
const translator = short();
|
||||||
|
|
||||||
|
export interface DailyVideoCallData {
|
||||||
|
type: string;
|
||||||
|
id: string;
|
||||||
|
password: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleErrorsJson(response) {
|
||||||
|
if (!response.ok) {
|
||||||
|
response.json().then(console.log);
|
||||||
|
throw Error(response.statusText);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dailyCredential = process.env.DAILY_API_KEY;
|
||||||
|
|
||||||
|
interface DailyVideoApiAdapter {
|
||||||
|
dailyCreateMeeting(event: CalendarEvent): Promise<any>;
|
||||||
|
|
||||||
|
dailyUpdateMeeting(uid: string, event: CalendarEvent);
|
||||||
|
|
||||||
|
dailyDeleteMeeting(uid: string): Promise<unknown>;
|
||||||
|
|
||||||
|
getAvailability(dateFrom, dateTo): Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DailyVideo = (credential): DailyVideoApiAdapter => {
|
||||||
|
const translateEvent = (event: CalendarEvent) => {
|
||||||
|
// Documentation at: https://docs.daily.co/reference#list-rooms
|
||||||
|
// added a 1 hour buffer for room expiration and room entry
|
||||||
|
const exp = Math.round(new Date(event.endTime).getTime() / 1000) + 60 * 60;
|
||||||
|
const nbf = Math.round(new Date(event.startTime).getTime() / 1000) - 60 * 60;
|
||||||
|
return {
|
||||||
|
privacy: "private",
|
||||||
|
properties: {
|
||||||
|
enable_new_call_ui: true,
|
||||||
|
enable_prejoin_ui: true,
|
||||||
|
enable_knocking: true,
|
||||||
|
enable_screenshare: true,
|
||||||
|
enable_chat: true,
|
||||||
|
exp: exp,
|
||||||
|
nbf: nbf,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getAvailability: () => {
|
||||||
|
return credential;
|
||||||
|
},
|
||||||
|
dailyCreateMeeting: (event: CalendarEvent) =>
|
||||||
|
fetch("https://api.daily.co/v1/rooms", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + dailyCredential,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(translateEvent(event)),
|
||||||
|
}).then(handleErrorsJson),
|
||||||
|
dailyDeleteMeeting: (uid: string) =>
|
||||||
|
fetch("https://api.daily.co/v1/rooms/" + uid, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + dailyCredential,
|
||||||
|
},
|
||||||
|
}).then(handleErrorsJson),
|
||||||
|
dailyUpdateMeeting: (uid: string, event: CalendarEvent) =>
|
||||||
|
fetch("https://api.daily.co/v1/rooms/" + uid, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + dailyCredential,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(translateEvent(event)),
|
||||||
|
}).then(handleErrorsJson),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// factory
|
||||||
|
const videoIntegrations = (withCredentials): DailyVideoApiAdapter[] =>
|
||||||
|
withCredentials
|
||||||
|
.map((cred) => {
|
||||||
|
return DailyVideo(cred);
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const getBusyVideoTimes: (withCredentials) => Promise<unknown[]> = (withCredentials) =>
|
||||||
|
Promise.all(videoIntegrations(withCredentials).map((c) => c.getAvailability())).then((results) =>
|
||||||
|
results.reduce((acc, availability) => acc.concat(availability), [])
|
||||||
|
);
|
||||||
|
|
||||||
|
const dailyCreateMeeting = async (
|
||||||
|
credential: Credential,
|
||||||
|
calEvent: CalendarEvent,
|
||||||
|
maybeUid: string = null
|
||||||
|
): Promise<EventResult> => {
|
||||||
|
const parser: CalEventParser = new CalEventParser(calEvent, maybeUid);
|
||||||
|
const uid: string = parser.getUid();
|
||||||
|
|
||||||
|
if (!credential) {
|
||||||
|
throw new Error(
|
||||||
|
"Credentials must be set! Video platforms are optional, so this method shouldn't even be called when no video credentials are set."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let success = true;
|
||||||
|
|
||||||
|
const creationResult = await videoIntegrations([credential])[0]
|
||||||
|
.dailyCreateMeeting(calEvent)
|
||||||
|
.catch((e) => {
|
||||||
|
log.error("createMeeting failed", e, calEvent);
|
||||||
|
success = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentRoute = process.env.BASE_URL;
|
||||||
|
|
||||||
|
const videoCallData: DailyVideoCallData = {
|
||||||
|
type: "Daily.co Video",
|
||||||
|
id: creationResult.name,
|
||||||
|
password: creationResult.password,
|
||||||
|
url: currentRoute + "/call/" + uid,
|
||||||
|
};
|
||||||
|
|
||||||
|
const entryPoint: EntryPoint = {
|
||||||
|
entryPointType: getIntegrationName(videoCallData),
|
||||||
|
uri: videoCallData.url,
|
||||||
|
label: "Enter Meeting",
|
||||||
|
pin: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const additionInformation: AdditionInformation = {
|
||||||
|
entryPoints: [entryPoint],
|
||||||
|
};
|
||||||
|
|
||||||
|
const organizerMail = new VideoEventOrganizerMail(calEvent, uid, videoCallData, additionInformation);
|
||||||
|
const attendeeMail = new VideoEventAttendeeMail(calEvent, uid, videoCallData, additionInformation);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await organizerMail.sendEmail();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("organizerMail.sendEmail failed", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!creationResult || !creationResult.disableConfirmationEmail) {
|
||||||
|
try {
|
||||||
|
await attendeeMail.sendEmail();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("attendeeMail.sendEmail failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "daily",
|
||||||
|
success,
|
||||||
|
uid,
|
||||||
|
createdEvent: creationResult,
|
||||||
|
originalEvent: calEvent,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const dailyUpdateMeeting = async (
|
||||||
|
credential: Credential,
|
||||||
|
uidToUpdate: string,
|
||||||
|
calEvent: CalendarEvent
|
||||||
|
): Promise<EventResult> => {
|
||||||
|
const newUid: string = translator.fromUUID(uuidv5(JSON.stringify(calEvent), uuidv5.URL));
|
||||||
|
|
||||||
|
if (!credential) {
|
||||||
|
throw new Error(
|
||||||
|
"Credentials must be set! Video platforms are optional, so this method shouldn't even be called when no video credentials are set."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let success = true;
|
||||||
|
|
||||||
|
const updateResult = credential
|
||||||
|
? await videoIntegrations([credential])[0]
|
||||||
|
.dailyUpdateMeeting(uidToUpdate, calEvent)
|
||||||
|
.catch((e) => {
|
||||||
|
log.error("updateMeeting failed", e, calEvent);
|
||||||
|
success = false;
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const organizerMail = new EventOrganizerRescheduledMail(calEvent, newUid);
|
||||||
|
const attendeeMail = new EventAttendeeRescheduledMail(calEvent, newUid);
|
||||||
|
try {
|
||||||
|
await organizerMail.sendEmail();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("organizerMail.sendEmail failed", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!updateResult || !updateResult.disableConfirmationEmail) {
|
||||||
|
try {
|
||||||
|
await attendeeMail.sendEmail();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("attendeeMail.sendEmail failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: credential.type,
|
||||||
|
success,
|
||||||
|
uid: newUid,
|
||||||
|
updatedEvent: updateResult,
|
||||||
|
originalEvent: calEvent,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const dailyDeleteMeeting = (credential: Credential, uid: string): Promise<unknown> => {
|
||||||
|
if (credential) {
|
||||||
|
return videoIntegrations([credential])[0].dailyDeleteMeeting(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve({});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getBusyVideoTimes, dailyCreateMeeting, dailyUpdateMeeting, dailyDeleteMeeting };
|
|
@ -25,11 +25,21 @@ export default class VideoEventAttendeeMail extends EventAttendeeMail {
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected getAdditionalBody(): string {
|
protected getAdditionalBody(): string {
|
||||||
return `
|
const meetingPassword = this.videoCallData.password;
|
||||||
|
const meetingId = getFormattedMeetingId(this.videoCallData);
|
||||||
|
|
||||||
|
if (meetingId && meetingPassword) {
|
||||||
|
return `
|
||||||
<strong>Video call provider:</strong> ${getIntegrationName(this.videoCallData)}<br />
|
<strong>Video call provider:</strong> ${getIntegrationName(this.videoCallData)}<br />
|
||||||
<strong>Meeting ID:</strong> ${getFormattedMeetingId(this.videoCallData)}<br />
|
<strong>Meeting ID:</strong> ${getFormattedMeetingId(this.videoCallData)}<br />
|
||||||
<strong>Meeting Password:</strong> ${this.videoCallData.password}<br />
|
<strong>Meeting Password:</strong> ${this.videoCallData.password}<br />
|
||||||
<strong>Meeting URL:</strong> <a href="${this.videoCallData.url}">${this.videoCallData.url}</a><br />
|
<strong>Meeting URL:</strong> <a href="${this.videoCallData.url}">${this.videoCallData.url}</a><br />
|
||||||
`;
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
<strong>Video call provider:</strong> ${getIntegrationName(this.videoCallData)}<br />
|
||||||
|
<strong>Meeting URL:</strong> <a href="${this.videoCallData.url}">${this.videoCallData.url}</a><br />
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,11 +26,19 @@ export default class VideoEventOrganizerMail extends EventOrganizerMail {
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected getAdditionalBody(): string {
|
protected getAdditionalBody(): string {
|
||||||
|
const meetingPassword = this.videoCallData.password;
|
||||||
|
const meetingId = getFormattedMeetingId(this.videoCallData);
|
||||||
// This odd indentation is necessary because otherwise the leading tabs will be applied into the event description.
|
// This odd indentation is necessary because otherwise the leading tabs will be applied into the event description.
|
||||||
return `
|
if (meetingPassword && meetingId) {
|
||||||
|
return `
|
||||||
<strong>Video call provider:</strong> ${getIntegrationName(this.videoCallData)}<br />
|
<strong>Video call provider:</strong> ${getIntegrationName(this.videoCallData)}<br />
|
||||||
<strong>Meeting ID:</strong> ${getFormattedMeetingId(this.videoCallData)}<br />
|
<strong>Meeting ID:</strong> ${getFormattedMeetingId(this.videoCallData)}<br />
|
||||||
<strong>Meeting Password:</strong> ${this.videoCallData.password}<br />
|
<strong>Meeting Password:</strong> ${this.videoCallData.password}<br />
|
||||||
|
<strong>Meeting URL:</strong> <a href="${this.videoCallData.url}">${this.videoCallData.url}</a><br />
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return `
|
||||||
|
<strong>Video call provider:</strong> ${getIntegrationName(this.videoCallData)}<br />
|
||||||
<strong>Meeting URL:</strong> <a href="${this.videoCallData.url}">${this.videoCallData.url}</a><br />
|
<strong>Meeting URL:</strong> <a href="${this.videoCallData.url}">${this.videoCallData.url}</a><br />
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import merge from "lodash.merge";
|
||||||
import { v5 as uuidv5 } from "uuid";
|
import { v5 as uuidv5 } from "uuid";
|
||||||
|
|
||||||
import { CalendarEvent, createEvent, updateEvent } from "@lib/calendarClient";
|
import { CalendarEvent, createEvent, updateEvent } from "@lib/calendarClient";
|
||||||
|
import { dailyCreateMeeting, dailyUpdateMeeting } from "@lib/dailyVideoClient";
|
||||||
import EventAttendeeMail from "@lib/emails/EventAttendeeMail";
|
import EventAttendeeMail from "@lib/emails/EventAttendeeMail";
|
||||||
import EventAttendeeRescheduledMail from "@lib/emails/EventAttendeeRescheduledMail";
|
import EventAttendeeRescheduledMail from "@lib/emails/EventAttendeeRescheduledMail";
|
||||||
import { LocationType } from "@lib/location";
|
import { LocationType } from "@lib/location";
|
||||||
|
@ -43,6 +44,9 @@ interface GetLocationRequestFromIntegrationRequest {
|
||||||
location: string;
|
location: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//const to idenfity a daily event location
|
||||||
|
const dailyLocation = "integrations:daily";
|
||||||
|
|
||||||
export default class EventManager {
|
export default class EventManager {
|
||||||
calendarCredentials: Array<Credential>;
|
calendarCredentials: Array<Credential>;
|
||||||
videoCredentials: Array<Credential>;
|
videoCredentials: Array<Credential>;
|
||||||
|
@ -55,6 +59,19 @@ export default class EventManager {
|
||||||
constructor(credentials: Array<Credential>) {
|
constructor(credentials: Array<Credential>) {
|
||||||
this.calendarCredentials = credentials.filter((cred) => cred.type.endsWith("_calendar"));
|
this.calendarCredentials = credentials.filter((cred) => cred.type.endsWith("_calendar"));
|
||||||
this.videoCredentials = credentials.filter((cred) => cred.type.endsWith("_video"));
|
this.videoCredentials = credentials.filter((cred) => cred.type.endsWith("_video"));
|
||||||
|
|
||||||
|
//for Daily.co video, temporarily pushes a credential for the daily-video-client
|
||||||
|
|
||||||
|
const hasDailyIntegration = process.env.DAILY_API_KEY;
|
||||||
|
const dailyCredential: Credential = {
|
||||||
|
id: +new Date().getTime(),
|
||||||
|
type: "daily_video",
|
||||||
|
key: { apikey: process.env.DAILY_API_KEY },
|
||||||
|
userId: +new Date().getTime(),
|
||||||
|
};
|
||||||
|
if (hasDailyIntegration) {
|
||||||
|
this.videoCredentials.push(dailyCredential);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -91,13 +108,25 @@ export default class EventManager {
|
||||||
);
|
);
|
||||||
|
|
||||||
const referencesToCreate: Array<PartialReference> = results.map((result: EventResult) => {
|
const referencesToCreate: Array<PartialReference> = results.map((result: EventResult) => {
|
||||||
return {
|
const isDailyResult = result.type === "daily";
|
||||||
type: result.type,
|
if (isDailyResult) {
|
||||||
uid: result.createdEvent.id.toString(),
|
return {
|
||||||
meetingId: result.videoCallData?.id.toString(),
|
type: result.type,
|
||||||
meetingPassword: result.videoCallData?.password,
|
uid: result.createdEvent.name.toString(),
|
||||||
meetingUrl: result.videoCallData?.url,
|
meetingId: result.videoCallData?.id.toString(),
|
||||||
};
|
meetingPassword: result.videoCallData?.password,
|
||||||
|
meetingUrl: result.videoCallData?.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!isDailyResult) {
|
||||||
|
return {
|
||||||
|
type: result.type,
|
||||||
|
uid: result.createdEvent.id.toString(),
|
||||||
|
meetingId: result.videoCallData?.id.toString(),
|
||||||
|
meetingPassword: result.videoCallData?.password,
|
||||||
|
meetingUrl: result.videoCallData?.url,
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -136,7 +165,8 @@ export default class EventManager {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const isDedicated = EventManager.isDedicatedIntegration(event.location);
|
const isDedicated =
|
||||||
|
EventManager.isDedicatedIntegration(event.location) || event.location === dailyLocation;
|
||||||
|
|
||||||
let results: Array<EventResult> = [];
|
let results: Array<EventResult> = [];
|
||||||
let optionalVideoCallData: VideoCallData | undefined = undefined;
|
let optionalVideoCallData: VideoCallData | undefined = undefined;
|
||||||
|
@ -198,6 +228,7 @@ export default class EventManager {
|
||||||
* @param optionalVideoCallData
|
* @param optionalVideoCallData
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private createAllCalendarEvents(
|
private createAllCalendarEvents(
|
||||||
event: CalendarEvent,
|
event: CalendarEvent,
|
||||||
noMail: boolean,
|
noMail: boolean,
|
||||||
|
@ -215,8 +246,10 @@ export default class EventManager {
|
||||||
* @param event
|
* @param event
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private getVideoCredential(event: CalendarEvent): Credential | undefined {
|
private getVideoCredential(event: CalendarEvent): Credential | undefined {
|
||||||
const integrationName = event.location.replace("integrations:", "");
|
const integrationName = event.location.replace("integrations:", "");
|
||||||
|
|
||||||
return this.videoCredentials.find((credential: Credential) => credential.type.includes(integrationName));
|
return this.videoCredentials.find((credential: Credential) => credential.type.includes(integrationName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +265,12 @@ export default class EventManager {
|
||||||
private createVideoEvent(event: CalendarEvent, maybeUid?: string): Promise<EventResult> {
|
private createVideoEvent(event: CalendarEvent, maybeUid?: string): Promise<EventResult> {
|
||||||
const credential = this.getVideoCredential(event);
|
const credential = this.getVideoCredential(event);
|
||||||
|
|
||||||
if (credential) {
|
const isDaily = event.location === dailyLocation;
|
||||||
|
|
||||||
|
if (credential && !isDaily) {
|
||||||
return createMeeting(credential, event, maybeUid);
|
return createMeeting(credential, event, maybeUid);
|
||||||
|
} else if (isDaily) {
|
||||||
|
return dailyCreateMeeting(credential, event, maybeUid);
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject("No suitable credentials given for the requested integration name.");
|
return Promise.reject("No suitable credentials given for the requested integration name.");
|
||||||
}
|
}
|
||||||
|
@ -271,8 +308,9 @@ export default class EventManager {
|
||||||
*/
|
*/
|
||||||
private updateVideoEvent(event: CalendarEvent, booking: PartialBooking) {
|
private updateVideoEvent(event: CalendarEvent, booking: PartialBooking) {
|
||||||
const credential = this.getVideoCredential(event);
|
const credential = this.getVideoCredential(event);
|
||||||
|
const isDaily = event.location === dailyLocation;
|
||||||
|
|
||||||
if (credential) {
|
if (credential && !isDaily) {
|
||||||
const bookingRef = booking.references.filter((ref) => ref.type === credential.type)[0];
|
const bookingRef = booking.references.filter((ref) => ref.type === credential.type)[0];
|
||||||
|
|
||||||
return updateMeeting(credential, bookingRef.uid, event).then((returnVal: EventResult) => {
|
return updateMeeting(credential, bookingRef.uid, event).then((returnVal: EventResult) => {
|
||||||
|
@ -283,6 +321,10 @@ export default class EventManager {
|
||||||
return returnVal;
|
return returnVal;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
if (isDaily) {
|
||||||
|
const bookingRefUid = booking.references.filter((ref) => ref.type === "daily")[0].uid;
|
||||||
|
return dailyUpdateMeeting(credential, bookingRefUid, event);
|
||||||
|
}
|
||||||
return Promise.reject("No suitable credentials given for the requested integration name.");
|
return Promise.reject("No suitable credentials given for the requested integration name.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,7 +342,8 @@ export default class EventManager {
|
||||||
*/
|
*/
|
||||||
private static isDedicatedIntegration(location: string): boolean {
|
private static isDedicatedIntegration(location: string): boolean {
|
||||||
// Hard-coded for now, because Zoom and Google Meet are both integrations, but one is dedicated, the other one isn't.
|
// Hard-coded for now, because Zoom and Google Meet are both integrations, but one is dedicated, the other one isn't.
|
||||||
return location === "integrations:zoom";
|
|
||||||
|
return location === "integrations:zoom" || location === dailyLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -313,7 +356,11 @@ export default class EventManager {
|
||||||
private static getLocationRequestFromIntegration(locationObj: GetLocationRequestFromIntegrationRequest) {
|
private static getLocationRequestFromIntegration(locationObj: GetLocationRequestFromIntegrationRequest) {
|
||||||
const location = locationObj.location;
|
const location = locationObj.location;
|
||||||
|
|
||||||
if (location === LocationType.GoogleMeet.valueOf() || location === LocationType.Zoom.valueOf()) {
|
if (
|
||||||
|
location === LocationType.GoogleMeet.valueOf() ||
|
||||||
|
location === LocationType.Zoom.valueOf() ||
|
||||||
|
location === LocationType.Daily.valueOf()
|
||||||
|
) {
|
||||||
const requestId = uuidv5(location, uuidv5.URL);
|
const requestId = uuidv5(location, uuidv5.URL);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -12,8 +12,8 @@ export function getIntegrationName(name: string) {
|
||||||
return "Stripe";
|
return "Stripe";
|
||||||
case "apple_calendar":
|
case "apple_calendar":
|
||||||
return "Apple Calendar";
|
return "Apple Calendar";
|
||||||
default:
|
case "daily_video":
|
||||||
return "Unknown";
|
return "Daily";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,5 @@ export enum LocationType {
|
||||||
Phone = "phone",
|
Phone = "phone",
|
||||||
GoogleMeet = "integrations:google:meet",
|
GoogleMeet = "integrations:google:meet",
|
||||||
Zoom = "integrations:zoom",
|
Zoom = "integrations:zoom",
|
||||||
|
Daily = "integrations:daily",
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@headlessui/react": "^1.4.1",
|
"@headlessui/react": "^1.4.1",
|
||||||
|
"@daily-co/daily-js": "^0.16.0",
|
||||||
"@heroicons/react": "^1.0.4",
|
"@heroicons/react": "^1.0.4",
|
||||||
"@hookform/resolvers": "^2.8.1",
|
"@hookform/resolvers": "^2.8.1",
|
||||||
"@jitsu/sdk-js": "^2.2.4",
|
"@jitsu/sdk-js": "^2.2.4",
|
||||||
|
@ -84,6 +85,7 @@
|
||||||
"react-select": "^4.3.1",
|
"react-select": "^4.3.1",
|
||||||
"react-timezone-select": "^1.0.7",
|
"react-timezone-select": "^1.0.7",
|
||||||
"react-use-intercom": "1.4.0",
|
"react-use-intercom": "1.4.0",
|
||||||
|
"react-router-dom": "^5.2.0",
|
||||||
"short-uuid": "^4.2.0",
|
"short-uuid": "^4.2.0",
|
||||||
"stripe": "^8.168.0",
|
"stripe": "^8.168.0",
|
||||||
"superjson": "1.7.5",
|
"superjson": "1.7.5",
|
||||||
|
|
|
@ -22,6 +22,13 @@ import { getBusyVideoTimes } from "@lib/videoClient";
|
||||||
import sendPayload from "@lib/webhooks/sendPayload";
|
import sendPayload from "@lib/webhooks/sendPayload";
|
||||||
import getSubscriberUrls from "@lib/webhooks/subscriberUrls";
|
import getSubscriberUrls from "@lib/webhooks/subscriberUrls";
|
||||||
|
|
||||||
|
export interface DailyReturnType {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
id: string;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
dayjs.extend(dayjsBusinessDays);
|
dayjs.extend(dayjsBusinessDays);
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
dayjs.extend(isBetween);
|
dayjs.extend(isBetween);
|
||||||
|
@ -249,7 +256,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
|
|
||||||
const attendeesList = [...invitee, ...guests, ...teamMembers];
|
const attendeesList = [...invitee, ...guests, ...teamMembers];
|
||||||
|
|
||||||
const seed = `${users[0].username}:${dayjs(reqBody.start).utc().format()}`;
|
const seed = `${users[0].username}:${dayjs(req.body.start).utc().format()}:${new Date().getTime()}`;
|
||||||
const uid = translator.fromUUID(uuidv5(seed, uuidv5.URL));
|
const uid = translator.fromUUID(uuidv5(seed, uuidv5.URL));
|
||||||
|
|
||||||
const evt: CalendarEvent = {
|
const evt: CalendarEvent = {
|
||||||
|
@ -353,8 +360,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
selectedCalendars
|
selectedCalendars
|
||||||
);
|
);
|
||||||
|
|
||||||
const videoBusyTimes = await getBusyVideoTimes(credentials);
|
const videoBusyTimes = (await getBusyVideoTimes(credentials)).filter((time) => time);
|
||||||
calendarBusyTimes.push(...videoBusyTimes);
|
calendarBusyTimes.push(...videoBusyTimes);
|
||||||
|
console.log("calendarBusyTimes==>>>", calendarBusyTimes);
|
||||||
|
|
||||||
const bufferedBusyTimes: BufferedBusyTimes = calendarBusyTimes.map((a) => ({
|
const bufferedBusyTimes: BufferedBusyTimes = calendarBusyTimes.map((a) => ({
|
||||||
start: dayjs(a.start).subtract(currentUser.bufferTime, "minute").toString(),
|
start: dayjs(a.start).subtract(currentUser.bufferTime, "minute").toString(),
|
||||||
|
@ -445,6 +453,46 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//for Daily.co video calls will grab the meeting token for the call
|
||||||
|
const isDaily = evt.location === "integrations:daily";
|
||||||
|
|
||||||
|
let dailyEvent: DailyReturnType;
|
||||||
|
|
||||||
|
if (!rescheduleUid) {
|
||||||
|
dailyEvent = results.filter((ref) => ref.type === "daily")[0]?.createdEvent as DailyReturnType;
|
||||||
|
} else {
|
||||||
|
dailyEvent = results.filter((ref) => ref.type === "daily_video")[0]?.updatedEvent as DailyReturnType;
|
||||||
|
}
|
||||||
|
|
||||||
|
let meetingToken;
|
||||||
|
if (isDaily) {
|
||||||
|
const response = await fetch("https://api.daily.co/v1/meeting-tokens", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ properties: { room_name: dailyEvent.name, is_owner: true } }),
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + process.env.DAILY_API_KEY,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
meetingToken = await response.json();
|
||||||
|
}
|
||||||
|
|
||||||
|
//for Daily.co video calls will update the dailyEventReference table
|
||||||
|
|
||||||
|
if (isDaily) {
|
||||||
|
await prisma.dailyEventReference.create({
|
||||||
|
data: {
|
||||||
|
dailyurl: dailyEvent.url,
|
||||||
|
dailytoken: meetingToken.token,
|
||||||
|
booking: {
|
||||||
|
connect: {
|
||||||
|
uid: booking.uid,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (eventType.requiresConfirmation && !rescheduleUid) {
|
if (eventType.requiresConfirmation && !rescheduleUid) {
|
||||||
await new EventOrganizerRequestMail(evt, uid).sendEmail();
|
await new EventOrganizerRequestMail(evt, uid).sendEmail();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import { deleteMeeting } from "@lib/videoClient";
|
||||||
import sendPayload from "@lib/webhooks/sendPayload";
|
import sendPayload from "@lib/webhooks/sendPayload";
|
||||||
import getSubscriberUrls from "@lib/webhooks/subscriberUrls";
|
import getSubscriberUrls from "@lib/webhooks/subscriberUrls";
|
||||||
|
|
||||||
|
import { dailyDeleteMeeting } from "../../lib/dailyVideoClient";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
// just bail if it not a DELETE
|
// just bail if it not a DELETE
|
||||||
if (req.method !== "DELETE" && req.method !== "POST") {
|
if (req.method !== "DELETE" && req.method !== "POST") {
|
||||||
|
@ -37,6 +39,7 @@ export default async function handler(req, res) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
attendees: true,
|
attendees: true,
|
||||||
|
location: true,
|
||||||
references: {
|
references: {
|
||||||
select: {
|
select: {
|
||||||
uid: true,
|
uid: true,
|
||||||
|
@ -118,6 +121,13 @@ export default async function handler(req, res) {
|
||||||
return await deleteMeeting(credential, bookingRefUid);
|
return await deleteMeeting(credential, bookingRefUid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//deleting a Daily meeting
|
||||||
|
|
||||||
|
const isDaily = bookingToDelete.location === "integrations:daily";
|
||||||
|
const bookingUID = bookingToDelete.references.filter((ref) => ref.type === "daily")[0]?.uid;
|
||||||
|
if (isDaily) {
|
||||||
|
return await dailyDeleteMeeting(credential, bookingUID);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bookingToDelete && bookingToDelete.paid) {
|
if (bookingToDelete && bookingToDelete.paid) {
|
||||||
|
|
90
pages/call/[uid].tsx
Normal file
90
pages/call/[uid].tsx
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import DailyIframe from "@daily-co/daily-js";
|
||||||
|
import { getSession } from "next-auth/client";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
import prisma from "../../lib/prisma";
|
||||||
|
|
||||||
|
export default function JoinCall(props, session) {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
//if no booking redirectis to the 404 page
|
||||||
|
const emptyBooking = props.booking === null;
|
||||||
|
useEffect(() => {
|
||||||
|
if (emptyBooking) {
|
||||||
|
router.push("/call/no-meeting-found");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!emptyBooking && session.userid !== props.booking.user.id) {
|
||||||
|
const callFrame = DailyIframe.createFrame({
|
||||||
|
showLeaveButton: true,
|
||||||
|
iframeStyle: {
|
||||||
|
position: "fixed",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
callFrame.join({
|
||||||
|
url: props.booking.dailyRef.dailyurl,
|
||||||
|
showLeaveButton: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!emptyBooking && session.userid === props.booking.user.id) {
|
||||||
|
const callFrame = DailyIframe.createFrame({
|
||||||
|
showLeaveButton: true,
|
||||||
|
iframeStyle: {
|
||||||
|
position: "fixed",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
callFrame.join({
|
||||||
|
url: props.booking.dailyRef.dailyurl,
|
||||||
|
showLeaveButton: true,
|
||||||
|
token: props.booking.dailyRef.dailytoken,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return JoinCall;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getServerSideProps(context) {
|
||||||
|
const booking = await prisma.booking.findFirst({
|
||||||
|
where: {
|
||||||
|
uid: context.query.uid,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
credentials: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
attendees: true,
|
||||||
|
dailyRef: {
|
||||||
|
select: {
|
||||||
|
dailyurl: true,
|
||||||
|
dailytoken: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
references: {
|
||||||
|
select: {
|
||||||
|
uid: true,
|
||||||
|
type: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const session = await getSession();
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
booking: booking,
|
||||||
|
session: session,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
52
pages/call/no-meeting-found.tsx
Normal file
52
pages/call/no-meeting-found.tsx
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { XIcon } from "@heroicons/react/outline";
|
||||||
|
import { ArrowRightIcon } from "@heroicons/react/solid";
|
||||||
|
|
||||||
|
import { HeadSeo } from "@components/seo/head-seo";
|
||||||
|
import Button from "@components/ui/Button";
|
||||||
|
|
||||||
|
export default function NoMeetingFound() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<HeadSeo title={`No meeting Found`} description={`No Meeting Found`} />
|
||||||
|
<main className="max-w-3xl mx-auto my-24">
|
||||||
|
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||||
|
<div className="flex items-end justify-center min-h-screen px-4 pt-4 pb-20 text-center sm:block sm:p-0">
|
||||||
|
<div className="fixed inset-0 my-4 transition-opacity sm:my-0" aria-hidden="true">
|
||||||
|
<span className="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">
|
||||||
|
​
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
className="inline-block px-4 pt-5 pb-4 overflow-hidden text-left align-bottom transition-all transform bg-white rounded-lg shadow-xl sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-labelledby="modal-headline">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center justify-center w-12 h-12 mx-auto bg-red-100 rounded-full">
|
||||||
|
<XIcon className="w-6 h-6 text-red-600" />
|
||||||
|
</div>
|
||||||
|
<div className="mt-3 text-center sm:mt-5">
|
||||||
|
<h3 className="text-lg font-medium leading-6 text-gray-900" id="modal-headline">
|
||||||
|
No Meeting Found
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<p className="text-sm text-center text-gray-500">
|
||||||
|
This meeting does not exist. Contact the meeting owner for an updated link.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-5 text-center sm:mt-6">
|
||||||
|
<div className="mt-5">
|
||||||
|
<Button data-testid="return-home" href="/event-types" EndIcon={ArrowRightIcon}>
|
||||||
|
Go back home
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,15 @@
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "DailyEventReference" (
|
||||||
|
"id" SERIAL NOT NULL,
|
||||||
|
"dailyurl" TEXT NOT NULL DEFAULT E'dailycallurl',
|
||||||
|
"dailytoken" TEXT NOT NULL DEFAULT E'dailytoken',
|
||||||
|
"bookingId" INTEGER,
|
||||||
|
|
||||||
|
PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "DailyEventReference_bookingId_unique" ON "DailyEventReference"("bookingId");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "DailyEventReference" ADD FOREIGN KEY ("bookingId") REFERENCES "Booking"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
|
@ -162,6 +162,14 @@ enum BookingStatus {
|
||||||
PENDING @map("pending")
|
PENDING @map("pending")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model DailyEventReference {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
dailyurl String @default("dailycallurl")
|
||||||
|
dailytoken String @default("dailytoken")
|
||||||
|
booking Booking? @relation(fields: [bookingId], references: [id])
|
||||||
|
bookingId Int?
|
||||||
|
}
|
||||||
|
|
||||||
model Booking {
|
model Booking {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
uid String @unique
|
uid String @unique
|
||||||
|
@ -179,6 +187,8 @@ model Booking {
|
||||||
attendees Attendee[]
|
attendees Attendee[]
|
||||||
location String?
|
location String?
|
||||||
|
|
||||||
|
dailyRef DailyEventReference?
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime?
|
updatedAt DateTime?
|
||||||
confirmed Boolean @default(true)
|
confirmed Boolean @default(true)
|
||||||
|
|
123
yarn.lock
123
yarn.lock
|
@ -282,15 +282,17 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.17", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.0":
|
"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.5", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.17", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.0":
|
||||||
version "7.15.4"
|
version "7.15.4"
|
||||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
|
||||||
|
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.12.13", "@babel/template@^7.15.4", "@babel/template@^7.3.3":
|
"@babel/template@^7.12.13", "@babel/template@^7.15.4", "@babel/template@^7.3.3":
|
||||||
version "7.15.4"
|
version "7.15.4"
|
||||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.15.4.tgz"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194"
|
||||||
|
integrity sha512-UgBAfEa1oGuYgDIPM2G+aHa4Nlo9Lh6mGD2bDBGMTbYnc38vulXPuC1MGjYILIEmlwl6Rd+BPR9ee3gm20CBtg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.14.5"
|
"@babel/code-frame" "^7.14.5"
|
||||||
"@babel/parser" "^7.15.4"
|
"@babel/parser" "^7.15.4"
|
||||||
|
@ -391,6 +393,17 @@
|
||||||
debug "^3.1.0"
|
debug "^3.1.0"
|
||||||
lodash.once "^4.1.1"
|
lodash.once "^4.1.1"
|
||||||
|
|
||||||
|
"@daily-co/daily-js@^0.16.0":
|
||||||
|
version "0.16.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@daily-co/daily-js/-/daily-js-0.16.0.tgz#9020104bb88de62dcc1966e713da65844243b9ab"
|
||||||
|
integrity sha512-DBWzbZs2IR7uYqfbABva1Ms3f/oX85dnQnCpVbGbexTN63LPIGknFSQp31ZYED88qcG+YJNydywBTb+ApNiNXA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.5"
|
||||||
|
bowser "^2.8.1"
|
||||||
|
events "^3.1.0"
|
||||||
|
fast-equals "^1.6.3"
|
||||||
|
lodash "^4.17.15"
|
||||||
|
|
||||||
"@emotion/cache@^11.4.0":
|
"@emotion/cache@^11.4.0":
|
||||||
version "11.4.0"
|
version "11.4.0"
|
||||||
resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz"
|
resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz"
|
||||||
|
@ -2478,6 +2491,11 @@ bn.js@^5.0.0, bn.js@^5.1.1:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz"
|
resolved "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz"
|
||||||
|
|
||||||
|
bowser@^2.8.1:
|
||||||
|
version "2.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f"
|
||||||
|
integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==
|
||||||
|
|
||||||
brace-expansion@^1.1.7:
|
brace-expansion@^1.1.7:
|
||||||
version "1.1.11"
|
version "1.1.11"
|
||||||
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
|
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
|
||||||
|
@ -3703,7 +3721,7 @@ eventemitter2@^6.4.3:
|
||||||
version "6.4.4"
|
version "6.4.4"
|
||||||
resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz"
|
resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.4.tgz"
|
||||||
|
|
||||||
events@^3.0.0:
|
events@^3.0.0, events@^3.1.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
|
resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz"
|
||||||
|
|
||||||
|
@ -3811,6 +3829,11 @@ fast-diff@^1.1.2:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz"
|
resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz"
|
||||||
|
|
||||||
|
fast-equals@^1.6.3:
|
||||||
|
version "1.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-1.6.3.tgz#84839a1ce20627c463e1892f2ae316380c81b459"
|
||||||
|
integrity sha512-4WKW0AL5+WEqO0zWavAfYGY1qwLsBgE//DN4TTcVEN2UlINgkv9b3vm2iHicoenWKSX9mKWmGOsU/iI5IST7pQ==
|
||||||
|
|
||||||
fast-glob@^3.1.1, fast-glob@^3.2.7:
|
fast-glob@^3.1.1, fast-glob@^3.2.7:
|
||||||
version "3.2.7"
|
version "3.2.7"
|
||||||
resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz"
|
resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz"
|
||||||
|
@ -4359,6 +4382,18 @@ highlight.js@^10.7.1:
|
||||||
version "10.7.3"
|
version "10.7.3"
|
||||||
resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz"
|
resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz"
|
||||||
|
|
||||||
|
history@^4.9.0:
|
||||||
|
version "4.10.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
|
||||||
|
integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.1.2"
|
||||||
|
loose-envify "^1.2.0"
|
||||||
|
resolve-pathname "^3.0.0"
|
||||||
|
tiny-invariant "^1.0.2"
|
||||||
|
tiny-warning "^1.0.0"
|
||||||
|
value-equal "^1.0.1"
|
||||||
|
|
||||||
hmac-drbg@^1.0.1:
|
hmac-drbg@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz"
|
||||||
|
@ -4367,7 +4402,7 @@ hmac-drbg@^1.0.1:
|
||||||
minimalistic-assert "^1.0.0"
|
minimalistic-assert "^1.0.0"
|
||||||
minimalistic-crypto-utils "^1.0.1"
|
minimalistic-crypto-utils "^1.0.1"
|
||||||
|
|
||||||
hoist-non-react-statics@^3.2.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
|
resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4826,6 +4861,11 @@ is-windows@^1.0.2:
|
||||||
resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||||
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
|
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
|
||||||
|
|
||||||
|
isarray@0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||||
|
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||||
|
|
||||||
isarray@^1.0.0, isarray@~1.0.0:
|
isarray@^1.0.0, isarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
|
||||||
|
@ -5795,9 +5835,10 @@ lodash.truncate@^4.4.2:
|
||||||
version "4.4.2"
|
version "4.4.2"
|
||||||
resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz"
|
resolved "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz"
|
||||||
|
|
||||||
lodash@4.17.21, lodash@4.x, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
|
lodash@4.17.21, lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
|
||||||
version "4.17.21"
|
version "4.17.21"
|
||||||
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||||
|
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||||
|
|
||||||
log-symbols@^4.0.0, log-symbols@^4.1.0:
|
log-symbols@^4.0.0, log-symbols@^4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
|
@ -5815,7 +5856,7 @@ log-update@^4.0.0:
|
||||||
slice-ansi "^4.0.0"
|
slice-ansi "^4.0.0"
|
||||||
wrap-ansi "^6.2.0"
|
wrap-ansi "^6.2.0"
|
||||||
|
|
||||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
|
resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -5949,6 +5990,14 @@ min-document@^2.19.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
dom-walk "^0.1.0"
|
dom-walk "^0.1.0"
|
||||||
|
|
||||||
|
mini-create-react-context@^0.4.0:
|
||||||
|
version "0.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
|
||||||
|
integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.1"
|
||||||
|
tiny-warning "^1.0.3"
|
||||||
|
|
||||||
mini-svg-data-uri@^1.2.3:
|
mini-svg-data-uri@^1.2.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.3.3.tgz"
|
resolved "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.3.3.tgz"
|
||||||
|
@ -6613,6 +6662,13 @@ path-parse@^1.0.6:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
|
resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
|
||||||
|
|
||||||
|
path-to-regexp@^1.7.0:
|
||||||
|
version "1.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
|
||||||
|
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
|
||||||
|
dependencies:
|
||||||
|
isarray "0.0.1"
|
||||||
|
|
||||||
path-type@^3.0.0:
|
path-type@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
|
resolved "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
|
||||||
|
@ -7140,7 +7196,7 @@ react-is@17.0.2, react-is@^17.0.1:
|
||||||
version "17.0.2"
|
version "17.0.2"
|
||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
|
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz"
|
||||||
|
|
||||||
react-is@^16.7.0, react-is@^16.8.1:
|
react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||||
|
|
||||||
|
@ -7188,6 +7244,35 @@ react-remove-scroll@^2.4.0:
|
||||||
use-callback-ref "^1.2.3"
|
use-callback-ref "^1.2.3"
|
||||||
use-sidecar "^1.0.1"
|
use-sidecar "^1.0.1"
|
||||||
|
|
||||||
|
react-router-dom@^5.2.0:
|
||||||
|
version "5.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.0.tgz#da1bfb535a0e89a712a93b97dd76f47ad1f32363"
|
||||||
|
integrity sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.13"
|
||||||
|
history "^4.9.0"
|
||||||
|
loose-envify "^1.3.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
react-router "5.2.1"
|
||||||
|
tiny-invariant "^1.0.2"
|
||||||
|
tiny-warning "^1.0.0"
|
||||||
|
|
||||||
|
react-router@5.2.1:
|
||||||
|
version "5.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.1.tgz#4d2e4e9d5ae9425091845b8dbc6d9d276239774d"
|
||||||
|
integrity sha512-lIboRiOtDLFdg1VTemMwud9vRVuOCZmUIT/7lUoZiSpPODiiH1UQlfXy+vPLC/7IWdFYnhRwAyNqA/+I7wnvKQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.12.13"
|
||||||
|
history "^4.9.0"
|
||||||
|
hoist-non-react-statics "^3.1.0"
|
||||||
|
loose-envify "^1.3.1"
|
||||||
|
mini-create-react-context "^0.4.0"
|
||||||
|
path-to-regexp "^1.7.0"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
react-is "^16.6.0"
|
||||||
|
tiny-invariant "^1.0.2"
|
||||||
|
tiny-warning "^1.0.0"
|
||||||
|
|
||||||
react-select@^4.3.1:
|
react-select@^4.3.1:
|
||||||
version "4.3.1"
|
version "4.3.1"
|
||||||
resolved "https://registry.npmjs.org/react-select/-/react-select-4.3.1.tgz"
|
resolved "https://registry.npmjs.org/react-select/-/react-select-4.3.1.tgz"
|
||||||
|
@ -7371,6 +7456,11 @@ resolve-from@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
|
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
|
||||||
|
|
||||||
|
resolve-pathname@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
|
||||||
|
integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
|
||||||
|
|
||||||
resolve@^1.10.0, resolve@^1.20.0:
|
resolve@^1.10.0, resolve@^1.20.0:
|
||||||
version "1.20.0"
|
version "1.20.0"
|
||||||
resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz"
|
resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz"
|
||||||
|
@ -8063,6 +8153,16 @@ timm@^1.6.1:
|
||||||
resolved "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f"
|
resolved "https://registry.npmjs.org/timm/-/timm-1.7.1.tgz#96bab60c7d45b5a10a8a4d0f0117c6b7e5aff76f"
|
||||||
integrity sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==
|
integrity sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==
|
||||||
|
|
||||||
|
tiny-invariant@^1.0.2:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||||
|
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
||||||
|
|
||||||
|
tiny-warning@^1.0.0, tiny-warning@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
|
||||||
|
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||||
|
|
||||||
tinycolor2@^1.4.1:
|
tinycolor2@^1.4.1:
|
||||||
version "1.4.2"
|
version "1.4.2"
|
||||||
resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
|
resolved "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
|
||||||
|
@ -8430,6 +8530,11 @@ validate-npm-package-license@^3.0.1:
|
||||||
spdx-correct "^3.0.0"
|
spdx-correct "^3.0.0"
|
||||||
spdx-expression-parse "^3.0.0"
|
spdx-expression-parse "^3.0.0"
|
||||||
|
|
||||||
|
value-equal@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
|
||||||
|
integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
|
||||||
|
|
||||||
verror@1.10.0:
|
verror@1.10.0:
|
||||||
version "1.10.0"
|
version "1.10.0"
|
||||||
resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz"
|
resolved "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz"
|
||||||
|
|
Loading…
Reference in a new issue